标签: micro-optimization

通过转换为uint而不是检查负值来执行范围检查是否更有效?

我在.NET的List源代码中偶然发现了这段代码:

// Following trick can reduce the range check by one
if ((uint) index >= (uint)_size) {
  ThrowHelper.ThrowArgumentOutOfRangeException();
}
Run Code Online (Sandbox Code Playgroud)

显然这比(?)更有效 if (index < 0 || index >= _size)

我很好奇这个技巧背后的理由.单个分支指令真的比两个转换要贵uint吗?或者是否还有一些其他优化会使这个代码比另外的数字比较更快?

为了解决房间里的大象:是的,这是微优化,不,我不打算在我的代码中到处使用它 - 我只是好奇;)

c# performance micro-optimization numeric-conversion range-checking

77
推荐指数
5
解决办法
4439
查看次数

是否有可能告诉分支预测器跟随分支的可能性有多大?

为了说清楚,我不打算在这里使用任何类型的便携性,所以任何将我绑定到某个盒子的解决方案都可以.

基本上,我有一个if语句将99%的时间评估为true,并且我试图剔除每个性能的最后一个时钟,我可以发出某种编译器命令(使用GCC 4.1.2和x86 ISA,如果告诉分支预测器它应该缓存该分支吗?

c x86 gcc micro-optimization compiler-optimization

73
推荐指数
4
解决办法
2万
查看次数

浮点除法与浮点乘法

通过编码是否有任何(非微优化)性能增益

float f1 = 200f / 2
Run Code Online (Sandbox Code Playgroud)

在比较中

float f2 = 200f * 0.5
Run Code Online (Sandbox Code Playgroud)

几年前我的一位教授告诉我,浮点除法比浮点乘法慢,但没有详细说明原因.

这句话适用于现代PC架构吗?

UPDATE1

关于评论,请同时考虑这个案例:

float f1;
float f2 = 2
float f3 = 3;
for( i =0 ; i < 1e8; i++)
{
  f1 = (i * f2 + i / f3) * 0.5; //or divide by 2.0f, respectively
}
Run Code Online (Sandbox Code Playgroud)

更新2 从评论中引用:

[我想]知道什么是算法/架构要求导致>除法在硬件上比复制要复杂得多

c++ floating-point micro-optimization

67
推荐指数
5
解决办法
5万
查看次数

在`typeid`代码中奇怪地使用`?:`

在我正在研究的其中一个项目中,我看到了这段代码

struct Base {
  virtual ~Base() { }
};

struct ClassX {
  bool isHoldingDerivedObj() const {
    return typeid(1 ? *m_basePtr : *m_basePtr) == typeid(Derived);
  }
  Base *m_basePtr;
};
Run Code Online (Sandbox Code Playgroud)

我从来没有见过这样的typeid用法.为什么它会做那种奇怪的舞蹈?:,而不仅仅是做typeid(*m_basePtr)?有什么理由吗?Base是一个多态类(带有虚拟析构函数).

编辑:在这个代码的另一个地方,我看到这个,它似乎是等价的"多余的"

template<typename T> T &nonnull(T &t) { return t; }

struct ClassY {
  bool isHoldingDerivedObj() const {
    return typeid(nonnull(*m_basePtr)) == typeid(Derived);
  }
  Base *m_basePtr;
};
Run Code Online (Sandbox Code Playgroud)

c++ conditional-operator typeid micro-optimization

60
推荐指数
2
解决办法
1967
查看次数

如何强制GCC假定浮点表达式为非负数?

在某些情况下,您知道某个浮点表达式将始终为非负数。例如,计算一个矢量的长度时,一个做sqrt(a[0]*a[0] + ... + a[N-1]*a[N-1])(NB:我知道的std::hypot,这是不相关的问题),并且平方根下表达显然是非负的。但是,GCC 为以下输出以下程序集sqrt(x*x)

        mulss   xmm0, xmm0
        pxor    xmm1, xmm1
        ucomiss xmm1, xmm0
        ja      .L10
        sqrtss  xmm0, xmm0
        ret
.L10:
        jmp     sqrtf
Run Code Online (Sandbox Code Playgroud)

也就是说,它将结果x*x与零进行比较,如果结果为非负数,则执行sqrtss指令,否则调用sqrtf

因此,我的问题是:如何强制GCC假定该x*x值始终为非负值,从而跳过比较和sqrtf调用,而无需编写内联汇编?

我想强调的是,我对本地解决方案感兴趣,而不是像-ffast-math-fno-math-errno或那样做-ffinite-math-only(尽管确实可以解决问题,这要归功于ks1322,harold和Eric Postpischil的评论)。

此外,“强制将GCC假定x*x为非负数”应解释为assert(x*x >= 0.f),因此这也排除了x*xNaN 的情况。

我可以使用特定于编译器,特定于平台,特定于CPU等的解决方案。

c++ floating-point assembly gcc micro-optimization

57
推荐指数
3
解决办法
2529
查看次数

为什么没有一个主要编译器优化这个检查值是否已经设置的条件存储?

我偶然发现了这篇Reddit 帖子,它是对以下代码片段的一个玩笑,

void f(int& x) {
    if (x != 1) {
        x = 1;
    }
}
void g(int& x) {
    x = 1;
}
Run Code Online (Sandbox Code Playgroud)

说这两个函数不等同于“编译器”。我确信任何主要的 C++ 编译器都会将条件赋值优化为无条件存储,从而为f和发出相同的汇编代码g

然而,他们没有。

谁能向我解释为什么会这样?

我的想法是:无条件存储很可能会更快,因为无论如何我们都必须访问内存来读取比较值,并且分支代码会给分支预测器带来压力。此外,编译器不应将存储视为副作用(AFAIK),即使后续内存访问可能会更快或更慢,具体取决于是否f由于缓存局部性而采用分支。

那么编译器就无法弄清楚这一点吗?虽然证明f和 的等价性g可能并不容易,但我觉得这些编译器能够解决更困难的问题。那么我可能错了,这些功能毕竟不相等,或者这里发生了什么?

c++ micro-optimization compiler-optimization

56
推荐指数
3
解决办法
5926
查看次数

使用xor reg,reg是否优于mov reg,0?

在x86上有两种众所周知的方法可以将整数寄存器设置为零值.

mov reg, 0
Run Code Online (Sandbox Code Playgroud)

要么

xor reg, reg
Run Code Online (Sandbox Code Playgroud)

有一种观点认为第二种变体更好,因为值0没有存储在代码中并且节省了几个字节的生成的机器代码.这绝对是好的 - 使用较少的指令缓存,这有时可以实现更快的代码执行.许多编译器生成这样的代码.

然而,在xor指令和改变相同寄存器的早期指令之间正式存在指令间依赖性.由于存在依赖性,后一条指令需要等到前者完成,这可能会减少处理器单元的负载并损害性能.

add reg, 17
;do something else with reg here
xor reg, reg
Run Code Online (Sandbox Code Playgroud)

很明显,无论初始寄存器值如何,xor的结果都将完全相同.但是处理器能够识别出这个吗?

我在VC++ 7中尝试了以下测试:

const int Count = 10 * 1000 * 1000 * 1000;
int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    DWORD start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            xor eax, eax
        };
    }
    DWORD diff = GetTickCount() - start;
    start = …
Run Code Online (Sandbox Code Playgroud)

x86 assembly micro-optimization

48
推荐指数
4
解决办法
1万
查看次数

45
推荐指数
4
解决办法
2万
查看次数

尽快比较形式(a + sqrt(b))的两个值?

作为我编写的程序的一部分,我需要比较形式为a + sqrt(b)where abunsigned integer的两个值。由于这是紧密循环的一部分,因此我希望此比较尽可能快地运行。(如果重要的话,我正在x86-64机器上运行代码,并且无符号整数不大于10 ^ 6。此外,我知道这样的事实a1<a2。)

作为独立功能,这就是我要优化的功能。我的数字是足够小的整数,可以double(或什至float)精确地表示它们,但是sqrt结果中的舍入误差不能改变结果。

// known pre-condition: a1 < a2  in case that helps
bool is_smaller(unsigned a1, unsigned b1, unsigned a2, unsigned b2) {
    return a1+sqrt(b1) < a2+sqrt(b2);  // computed mathematically exactly
}
Run Code Online (Sandbox Code Playgroud)

测试用例is_smaller(900000, 1000000, 900001, 998002)应返回true,但如@wim注释所示,sqrtf()将其返回false。因此将(int)sqrt()截断为整数。

a1+sqrt(b1) = 90100a2+sqrt(b2) = 901000.00050050037512481206。最接近的浮点数就是90100。


由于sqrt()即使在现代的x86-64上作为sqrtsd指令完全内联时,该函数通常也非常昂贵,所以我试图避免sqrt()尽可能地调用。

通过平方运算删除sqrt还可以通过使所有计算都精确来避免舍入错误的任何危险。

如果相反,功能是这样的...

bool is_smaller(unsigned …
Run Code Online (Sandbox Code Playgroud)

c++ optimization algebra micro-optimization sqrt

45
推荐指数
1
解决办法
1737
查看次数

在Doom 3 BFG代码中计算Sqrt(x)为x*InvSqrt(x)是否有意义?

我浏览了最近发布的Doom 3 BFG源代码,当时我遇到了一些似乎没有任何意义的东西.Doom 3在idMath类中包含数学函数.一些函数只是向相应的函数提供math.h,但有些是重新实现(例如idMath :: exp16()),我认为它具有比它们的math.h对应物更高的性能(可能以牺牲精度为代价).

然而,令我感到困惑的是他们实现这一float idMath::Sqrt(float x)功能的方式:

ID_INLINE float idMath::InvSqrt( float x ) {
     return ( x > FLT_SMALLEST_NON_DENORMAL ) ? sqrtf( 1.0f / x ) : INFINITY;
}

ID_INLINE float idMath::Sqrt( float x ) {
     return ( x >= 0.0f ) ? x * InvSqrt( x ) : 0.0f;
}
Run Code Online (Sandbox Code Playgroud)

这似乎执行两个不必要的浮点运算:首先是除法然后是乘法.

值得注意的是,原始的Doom 3源代码也以这种方式实现了平方根函数,但是反平方根使用了快速平方根算法.

ID_INLINE float idMath::InvSqrt( float x ) {

    dword …
Run Code Online (Sandbox Code Playgroud)

c++ optimization math.h micro-optimization

44
推荐指数
2
解决办法
2492
查看次数