标签: micro-optimization

为什么在python中的空函数调用对于动态编译的python代码来说要慢15%左右

这是非常糟糕的微优化,但我只是好奇.它通常不会对"真实"世界产生影响.

所以我正在编译一个函数(什么都不做),compile()然后调用exec该代码并获取对我编译的函数的引用.然后我执行了几百万次计时.然后用本地函数重复它.为什么动态编译的函数只需要调用约15%(在python 2.7.2上)?

import datetime
def getCompiledFunc():
  cc = compile("def aa():pass", '<string>', 'exec')
  dd = {}
  exec cc in dd
  return dd.get('aa')

compiledFunc = getCompiledFunc()  
def localFunc():pass


def testCall(f):
  st = datetime.datetime.now()
  for x in xrange(10000000): f()
  et = datetime.datetime.now()
  return (et-st).total_seconds()

for x in xrange(10):
  lt = testCall(localFunc)
  ct = testCall(compiledFunc)
  print "%s %s %s%% slower" % (lt, ct, int(100.0*(ct-lt)/lt))
Run Code Online (Sandbox Code Playgroud)

我得到的输出是这样的:

1.139 1.319 15% slower
Run Code Online (Sandbox Code Playgroud)

python optimization dynamic micro-optimization python-2.7

7
推荐指数
1
解决办法
603
查看次数

实现vptr的替代方案?

这个问题不是关于C++语言本身(即不是关于标准),而是关于如何调用编译器来实现虚函数的替代方案.

实现虚函数的一般方案是使用指向指针表的指针.

class Base {
     private:
        int m;
     public:
        virtual metha();
};
Run Code Online (Sandbox Code Playgroud)

等价地说C会是这样的

struct Base {
    void (**vtable)();
    int m;
}
Run Code Online (Sandbox Code Playgroud)

第一个成员通常是指向虚拟函数列表等的指针(应用程序无法控制的内存中的一块区域).在大多数情况下,这会在考虑成员之前花费指针的大小等等.因此在大约4个字节的32位寻址方案中等等.如果在应用程序中创建了40k多态对象的列表,则大约为40k x在任何成员变量等之前4个字节= 160k字节.我也知道这恰好是C++编译中最快和最常见的实现.

我知道多重继承很复杂(尤其是虚拟类,即菱形结构等).

另一种方法是将第一个变量作为vptrs表的索引id(等效于C,如下所示)

struct Base {
    char    classid;     // the classid here is an index into an array of vtables
    int     m;
}
Run Code Online (Sandbox Code Playgroud)

如果应用程序中的类总数小于255(包括所有可能的模板实例化等),则char足以保存索引,从而减少应用程序中所有多态类的大小(我排除了对齐问题)等).

我的问题是,在GNU C++,LLVM或任何其他编译器中是否有任何切换来执行此操作?或减少多态对象的大小?

编辑:我了解指出的对齐问题.还有一点,如果这是64位系统(假设为64位vptr),每个多态对象成员的成本约为8字节,那么vptr的成本就是内存的50%.这主要涉及大量创建的小型多态,所以我想知道如果不是整个应用程序,这个方案是否至少可以用于特定的虚拟对象.

c++ compiler-construction micro-optimization compiler-optimization vptr

7
推荐指数
1
解决办法
782
查看次数

非虚拟接口?(需要一个非常高性能的低级抽象)

我正在尝试在应用程序架构中的非常低级别对我的代码进行微优化.所以这是我的具体情况:

  • 我有一个解析图形文件的解析器类(节点,边,邻接条目等)
  • 文件格式是版本化的,因此每个版本都存在解析器,这些解析器实现为单独的类(ParserV1,ParserV2,...).
  • 解析器为应用程序中的某些上层提供相同的功能.因此,它们实现相同的" 界面 ".
  • 在C++中,我将这样的接口实现为抽象类,所有函数都是纯虚拟的.
  • 由于虚函数需要另一个内存查找,并且在编译时不能静态绑定 - 更重要的是 - 不允许在解析器类中内联小方法,使用经典的子类成语不会导致我能达到的最佳表现.

[在描述我可能的解决方案之前,我想解释为什么我在这里进行微优化(你可以跳过这一段):解析器类有很多小方法,其中"小"意味着它们没有做太多.它们中的大多数只从缓存的比特流中读取一个或两个字节,甚至只读取一个比特.所以应该可以以非常有效的方式实现它们,其中函数调用在内联时只需要少量的机器命令.这些方法在应用程序中经常被调用,因为它们在一个非常大的图形(全球道路网络)中查找节点属性,每个用户请求可能会发生大约一百万次,并且这样的请求应该像可能.]

去哪儿路?我可以看到以下方法来解决问题:

  1. 使用纯虚方法编写接口并将其子类化.表现将受到影响.
  2. 不要写这样的界面.每个解析器自己定义相同的方法.在上层(使用解析器)具有指向每个版本子类的指针(作为成员).在开始时,实例化应该使用的特定解析器.使用switch块并在访问函数时将解析器实例强制转换为显式子类.表现会更好吗?(if/switch block与虚拟表查找).
  3. 混合两种解决方案1. + 2:使用纯虚方法编写一个接口,用于很少使用的方法,其中性能不是很关键.如果它很重要,请不要提供虚方法,而是使用第二种方法.
  4. 改进2:在抽象类中提供非虚方法; 将版本号作为成员变量保留在抽象类中(一种自己的运行时类型信息),并在这些方法中实现if/switch块和强制转换; 然后调用子类中的方法.这提供了内联和静态绑定.

有没有更好的方法来解决这个问题?这有什么成语吗?

为了澄清,我有很多与版本无关的函数(至少到现在为止),因此非常适合某些超类.我将对大多数函数使用标准的子类设计,而这个问题仅涵盖要优化的版本相关函数的解决方案.(其中一些不经常被调用,我当然可以在这些情况下使用虚方法.)除此之外,我不喜欢让解析器类决定哪些方法需要高性能而哪些方法无效.(虽然有可能这样做.)

c++ architecture abstraction idioms micro-optimization

7
推荐指数
1
解决办法
564
查看次数

性能:typedef vs原始类型的包装类?

我想在C++中定义一个新类型,它只是一些原始类型(在我的例子中int,可以是任何类型).我NodeId在这个例子中调用了这个类型.

我可以用typedef int NodeId.我想要一个NodeIds 的默认值,所以我会用#define NULL_NODE_ID -1.

现在,我认为定义一个类而不是typedef允许一个函数isValid()和构造一个null的默认构造函数会更好NodeId:

class NodeId
{
    int value;
public:
    inline NodeId() : value(-1) {}
    inline NodeId(int value) : value(value) {}
    inline operator int() {return value;}
    inline bool isValid() {return value != -1;}
    //...
};
Run Code Online (Sandbox Code Playgroud)

是否存在导致使用第二种方法的性能缺点?

c++ inline micro-optimization

7
推荐指数
1
解决办法
3418
查看次数

微优化:使用局部变量与类成员进行迭代

如果我将一次迭代变量声明为类成员,我想我会节省一些时间:

struct Foo {
  int i;
  void method1() {
    for(i=0; i<A; ++i) ...
  }
  void method2() {
    for(i=0; i<B; ++i) ...
  }
} foo;
Run Code Online (Sandbox Code Playgroud)

然而,这似乎快了20%

struct Foo {
  void method1() {
    for(int i=0; i<A; ++i) ...
  }
  void method2() {
    for(int i=0; i<B; ++i) ...
  }
} foo;
Run Code Online (Sandbox Code Playgroud)

在这段代码中

void loop() { // Arduino loops
  foo.method1();
  foo.method2();
}
Run Code Online (Sandbox Code Playgroud)

你能解释性能差异吗?

(我需要在Arduino上运行许多简单的paralel"进程",这样的微优化会产生影响.)

c++ micro-optimization

7
推荐指数
1
解决办法
138
查看次数

x> = 0比x> -1更有效吗?

在C++中使用int进行x >= 0比较比效率更高x > -1

c++ optimization micro-optimization

6
推荐指数
3
解决办法
717
查看次数

java micro-optimization:将一组布尔实例变量与基于int的位向量相结合

我们有一个包含许多实例的类,并遇到内存问题.因此,我们尝试减少这个类的内存需求.一个想法是以下.

该类有许多布尔实例变量,每个变量在初始实现中占用一个单词.可以想到将它们组合到存储在int中的迷你位向量,使得它们的组合存储器要求将是一个字.

但我怀疑Java VM正在进行这种优化,因此手动执行它不会获得任何额外的节省.对?

java boolean micro-optimization bitvector

6
推荐指数
2
解决办法
184
查看次数

python"elif"的编译方式是否与else不同:if?

我知道在C,C++,Java和C#等语言中,(C#示例)else if语句是语法糖,因为它实际上只是一个else语句后跟一个if语句.

else if (conition(s)) { ...
Run Code Online (Sandbox Code Playgroud)

等于

else {
    if (condition(s)) { ...
}
Run Code Online (Sandbox Code Playgroud)

但是,在python中,有一个特殊的elif声明.我一直想知道这是否只是开发人员的简写,或者是否有一些隐藏的优化python可以做到这一点,比如更快解释?但这对我来说没有意义,因为其他语言也会这样做(比如JavaScript).所以,我的问题是,在python中,elif语句只是开发人员使用的简写,还是隐藏了它通过这样做而获得的东西?

c++ python java if-statement micro-optimization

6
推荐指数
1
解决办法
361
查看次数

函数所需的堆栈空间是否会影响C/C++中的内联决策?

函数需要大量的堆栈空间是否会阻止它内联?例如,如果我在堆栈上有一个10k自动缓冲区,是否会使该函数不太可能被内联?

int inlineme(int args) {
  char svar[10000];

  return stringyfunc(args, svar);
}
Run Code Online (Sandbox Code Playgroud)

我更关心gcc,但icc和llvm也很高兴知道.

我知道这不太理想,但我很好奇.缓存上的代码很可能也很糟糕.

c c++ gcc inline micro-optimization

6
推荐指数
1
解决办法
243
查看次数

在__uint128_t上最有效的popcount?

我需要以最有效(最快)的方式来弹出大小为128位的无符号变量。

  • 操作系统:Linux / Debian 9
  • 编译器:GCC 8
  • 处理器:Intel i7-5775C

尽管解决方案便携,甚至更好。

首先,GCC中有两种类型,分别是__uint128_tunsigned __int128。我猜他们最终还是一样,看不出有什么理由写丑陋的unsigned __int128东西,因此尽管它应该是新类型,但我更喜欢第一个,它与标准更加相似uint64_t。另外,英特尔拥有__uint128_t使用它的另一个原因(可移植性)。

我写了以下代码:

#include <nmmintrin.h>
#include <stdint.h>

static inline   uint_fast8_t    popcnt_u128 (__uint128_t n)
{
    const uint64_t      n_hi    = n >> 64;
    const uint64_t      n_lo    = n;
    const uint_fast8_t  cnt_hi  = _mm_popcnt_u64(n_hi);
    const uint_fast8_t  cnt_lo  = _mm_popcnt_u64(n_lo);
    const uint_fast8_t  cnt     = cnt_hi + cnt_lo;

    return  cnt;
}
Run Code Online (Sandbox Code Playgroud)

这是绝对最快的选择吗?

编辑:

我想到了另一个选择,它可能会(或不会)更快:

#include <nmmintrin.h>
#include <stdint.h>

union   Uint128 {
    __uint128_t …
Run Code Online (Sandbox Code Playgroud)

c gcc x86-64 intel micro-optimization

6
推荐指数
1
解决办法
319
查看次数