相关疑难解决方法(0)

严格别名的性能优势

在C中,观察严格混叠会带来什么样的性能优势?

c performance

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

使用c - printf将ieee 754 float转换为hex

理想情况下,以下代码将采用IEEE 754表示形式的浮点数并将其转换为十六进制

void convert() //gets the float input from user and turns it into hexadecimal
{
    float f;
    printf("Enter float: ");
    scanf("%f", &f);
    printf("hex is %x", f);
}
Run Code Online (Sandbox Code Playgroud)

我不太确定会出现什么问题.它将数字转换为十六进制数,但却是一个非常错误的数字.

123.1443 gives 40000000
43.3     gives 60000000
8        gives 0
Run Code Online (Sandbox Code Playgroud)

所以它正在做某事,我只是不太确定是什么.

帮助将不胜感激

c floating-point ieee-754

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

严格别名和内存对齐

我有性能关键代码,并且有一个巨大的函数,在函数开始时在堆栈上分配40个不同大小的数组.这些阵列中的大多数必须具有一定的对齐性(因为这些阵列是使用需要内存对齐的cpu指令(对于Intel和arm CPU)在链中的其他位置访问的.

由于某些版本的gcc无法正确对齐堆栈变量(特别是对于arm代码),或者甚至有时它表示目标体系结构的最大对齐小于我的代码实际请求的对齐,我别无选择,只能分配这些数组在堆栈上并手动对齐它们.

所以,对于每个数组,我需要做类似的事情才能使它正确对齐:

short history_[HIST_SIZE + 32];
short * history = (short*)((((uintptr_t)history_) + 31) & (~31));
Run Code Online (Sandbox Code Playgroud)

这样,history现在在32字节边界上对齐.对所有40个数组执行相同的操作非常繁琐,而且这部分代码实际上是cpu密集型的,我根本无法对每个数组执行相同的对齐技术(这种对齐混乱会使优化器和不同的寄存器分配混淆,从而减慢函数的使用时间,为了更好的解释,请参阅问题末尾的解释).

所以......显然,我只想做一次手动对齐,并假设这些数组一个接着一个.我还为这些数组添加了额外的填充,以便它们总是32个字节的倍数.那么,我只需在堆栈上创建一个jumbo char数组并将其转换为具有所有这些对齐数组的结构:

struct tmp
{
   short history[HIST_SIZE];
   short history2[2*HIST_SIZE];
   ...
   int energy[320];
   ...
};


char buf[sizeof(tmp) + 32];
tmp * X = (tmp*)((((uintptr_t)buf) + 31) & (~31));
Run Code Online (Sandbox Code Playgroud)

这样的事情.也许不是最优雅的,但它产生了非常好的结果,并且对生成的组件的手动检查证明生成的代码或多或少是足够的和可接受的.构建系统已更新为使用更新的GCC,突然我们开始在生成的数据中有一些工件(例如,即使在具有禁用的asm代码的纯C构建中,验证测试套件的输出也不再精确).调试问题花了很长时间,它似乎与别名规则和更新版本的GCC有关.

那么,我该怎么做呢?请不要浪费时间试图解释它不是标准的,不是可移植的,未定义的等等(我已经阅读过很多关于此的文章).此外,我无法改变代码(我可能会考虑修改GCC以解决问题,但不能重构代码)...基本上,我想要的是应用一些黑魔法咒语以便更新的GCC为这种类型的代码生成功能相同的代码而不禁用优化?

编辑:

  • 我在多个操作系统/编译器上使用了这个代码,但是当我切换到基于GCC 4.6的更新的NDK时开始出现问题.我用GCC 4.7得到了同样糟糕的结果(来自NDK r8d)
  • 我提到32字节对齐.如果它伤害了你的眼睛,用你喜欢的任何其他数字代替它,例如666,如果它有帮助.毫无疑问,大多数架构都不需要这种对齐.如果我在堆栈上对齐8KB的本地数组,则为16字节对齐松散15个字节,而对于32字节对齐,我松散31个字节.我希望我的意思很清楚.
  • 我说在性能关键代码中堆栈上有40个阵列.我可能还需要说它是一个运行良好的第三方旧代码,我不想搞砸它.没有必要说它是好还是坏,没有意义.
  • 此代码/函数具有经过良好测试和定义的行为.我们对该代码的要求具有确切的数量,例如它分配Xkb或RAM,使用Y kb的静态表,并且消耗多达Z kb的堆栈空间并且它不能改变,因为代码不会被改变.
  • 通过说"对齐混乱混淆优化器"我的意思是,如果我尝试单独对齐每个数组代码优化器为对齐代码分配额外的寄存器和性能关键代码部分突然没有足够的寄存器并开始垃圾堆栈而不是导致代码速度减慢.在ARM CPU上观察到这种行为(顺便说一句,我根本不担心英特尔).
  • 通过工件,我的意思是输出变为非bitexact,增加了一些噪音.要么是因为这种类型的别名问题,要么编译器中存在一些错误,最终导致函数输出错误.

    简而言之,问题的关键点......我如何分配随机数量的堆栈空间(使用char数组或者alloca,然后将指针对齐到该堆栈空间并重新解释这个内存块,因为某些结构具有一些定义良好的布局,只要结构本身正确对齐,我就会保证某些变量的对齐.我正在尝试使用各种方法来转换内存,我将大堆栈分配移动到一个单独的函数,仍然会导致输出错误和堆栈损坏,我我真的开始越来越多地认为这个巨大的功能会在gcc中遇到某种错误.这很奇怪,通过这样做,无论我尝试什么,我都无法完成这件事.顺便说一下,我禁用了所有需要任何对齐的优化,现在都是纯C风格的代码,我仍然得到不好的结果(非bitexact输出和偶尔的堆栈损坏崩溃).修复它的简单修复,我写而不是:

    char buf[sizeof(tmp) + 32];
    tmp * X = (tmp*)((((uintptr_t)buf) + 31) & (~31));
    
    Run Code Online (Sandbox Code Playgroud)

    这段代码:

    tmp …
    Run Code Online (Sandbox Code Playgroud)
  • c c++ casting type-punning

    17
    推荐指数
    2
    解决办法
    2405
    查看次数

    如何实现"_mm_storeu_epi64"没有别名问题?

    (注意:虽然这个问题是关于"存储"的,但"加载"情况具有相同的问题并且是完全对称的.)

    SSE内在函数提供_mm_storeu_pd具有以下签名的函数:

    void _mm_storeu_pd (double *p, __m128d a);
    
    Run Code Online (Sandbox Code Playgroud)

    所以,如果我有两个双精度矢量,并且我想将它存储到两个双精度数组中,我可以使用这个内在函数.

    但是,我的矢量不是两个双打; 它是两个64位整数,我想将它存储到两个64位整数的数组中.也就是说,我想要一个具有以下签名的函数:

    void _mm_storeu_epi64 (int64_t *p, __m128i a);
    
    Run Code Online (Sandbox Code Playgroud)

    但内在函数没有提供这样的功能.他们最接近的是_mm_storeu_si128:

    void _mm_storeu_si128 (__m128i *p, __m128i a);
    
    Run Code Online (Sandbox Code Playgroud)

    问题是这个函数需要一个指针__m128i,而我的数组是一个数组int64_t.通过错误类型的指针写入对象违反了严格的别名,并且肯定是未定义的行为.我担心我的编译器现在或将来会重新排序或以其他方式优化存储,从而以奇怪的方式破坏我的程序.

    要清楚,我想要的是一个我可以这样调用的函数:

    __m128i v = _mm_set_epi64x(2,1);
    int64_t ra[2];
    _mm_storeu_epi64(&ra[0], v); // does not exist, so I want to implement it
    
    Run Code Online (Sandbox Code Playgroud)

    以下是创建此类功能的六次尝试.

    尝试#1

    void _mm_storeu_epi64(int64_t *p, __m128i a) {
        _mm_storeu_si128(reinterpret_cast<__m128i *>(p), a);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这似乎有我担心的严格别名问题.

    尝试#2

    void _mm_storeu_epi64(int64_t *p, __m128i a) {
        _mm_storeu_si128(static_cast<__m128i *>(static_cast<void *>(p)), a);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    一般来说可能更好 …

    c++ sse strict-aliasing intrinsics

    17
    推荐指数
    1
    解决办法
    1506
    查看次数

    什么是别名以及它如何影响性能?

    在GoingNative活动中,在第2天的交互式面板中,在9分钟时,Chandler Carruth说:

    指针会产生锯齿问题.他们放慢你的二进制文件速度而不加速它们.

    这是什么意思?这可以用(简单)示例来说明吗?

    c++

    16
    推荐指数
    2
    解决办法
    1万
    查看次数

    如何避免在基于B树的类似STL的地图中浪费复制密钥?

    我更换使用std::map与热路径CPP-B树btree_map.但是在启用优化的情况下,GCC和Clang抱怨严格的别名违规.问题归结为:

    template <typename Key, typename Value>
    class btree_map {
    public:
        // In order to match the standard library's container interfaces
        using value_type = std::pair<const Key, Value>;
    
    private:
        using mutable_value_type = std::pair<Key, Value>;
    
        struct node_type {
            mutable_value_type values[N];
            // ...
        };
    
    public:
        class iterator {
            // ...
    
            value_type& operator*() {
                // Here we cast from const std::pair<Key, Value>&
                // to const std::pair<const Key, Value>&
                return reinterpret_cast<value_type&>(node->values[i]);
            }
        };
    
        std::pair<iterator, bool> insert(const value_type& value) {
            // …
    Run Code Online (Sandbox Code Playgroud)

    c++ b-tree move undefined-behavior c++11

    16
    推荐指数
    1
    解决办法
    636
    查看次数

    将字节数组转换为POD

    比方说,我有一组无符号字符代表一堆POD对象(例如从套接字或通过mmap读取).它们代表哪些类型以及在运行时确定的位置,但我们假设每个类型已经正确对齐.

    将这些字节"转换"为相应的POD类型的最佳方法是什么?

    解决方案应该符合c ++标准(比方说> = c ++ 11)或者至少可以保证使用g ++> = 4.9,clang ++> = 3.5和MSVC> = 2015U3.编辑:在Linux,Windows上运行x86/x64或32/64位臂.

    理想情况下,我想做这样的事情:

    uint8_t buffer[100]; //filled e.g. from network
    
    switch(buffer[0]) {
        case 0: process(*reinterpret_cast<Pod1*>(&buffer[4]); break;
        case 1: process(*reinterpret_cast<Pod2*>(&buffer[8+buffer[1]*4]); break;
        //...
    }
    
    Run Code Online (Sandbox Code Playgroud)

    要么

    switch(buffer[0]) {
        case 0: {
             auto* ptr = new(&buffer[4]) Pod1; 
             process(*ptr); 
        }break;
        case 1: {
             auto* ptr = new(&buffer[8+buffer[1]*4]) Pod2; 
             process(*ptr); 
        }break;
        //...
    }
    
    Run Code Online (Sandbox Code Playgroud)

    两者似乎都有效,但两者都是c ++中的AFAIK未定义行为1).而且只是为了完整性:我知道将通常的东西复制到适当的局部变量中的"通常"解决方案:

     Pod1 tmp;
     std::copy_n(&buffer[4],sizeof(tmp), reinterpret_cast<uint8_t*>(&tmp));             
     process(tmp); 
    
    Run Code Online (Sandbox Code Playgroud)

    在某些情况下,它可能不是其他人的开销,在某些情况下甚至可能更快,但性能除外,我不再能够修改数据并且说实话:它让我很生气,知道我有右位在内存中的适当位置,但我不能使用它们.


    我想出的一个有点疯狂的解决方案是:

    template<class T>
    T* inplace_cast(uint8_t* data) {
        //checks omitted for …
    Run Code Online (Sandbox Code Playgroud)

    c++ strict-aliasing

    16
    推荐指数
    1
    解决办法
    657
    查看次数

    如何实现没有未定义行为的快速逆sqrt?

    根据我对严格别名规则的理解,这个快速反平方根的代码将导致C++中未定义的行为:

    float Q_rsqrt( float number )
    {
        long i;
        float x2, y;
        const float threehalfs = 1.5F;
    
        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y; // type punning
        i  = 0x5f3759df - ( i >> 1 );
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );
    
        return y;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这段代码确实会导致UB吗?如果是,如何以符合标准的方式重新实现?如果没有,为什么不呢?

    假设:在调用此函数之前,我们已经以某种方式检查了浮点数是IEEE 754 32位格式, …

    c++ strict-aliasing undefined-behavior

    15
    推荐指数
    2
    解决办法
    1496
    查看次数

    变量是一个大小为1的数组吗?

    考虑一下:

    int main(int, char **) {
      int variable = 21;
      int array[1] = {21};
      using ArrayOf1Int = int[1];
      (*reinterpret_cast<ArrayOf1Int *>(&variable))[0] = 42;
      *reinterpret_cast<int *>(&array) = 42;
      return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    我是否违反了严格的别名规则

    或者,正如在这条引导我这个问题的评论中一样:变量是一个大小为1的数组吗?

    请注意,我将此标记为语言律师问题.因此,我对-fno-strict-aliasing编译器的特定行为不感兴趣,而是在标准中所说的内容.另外我认为知道C++ 03,C++ 11,C++ 14和更新版本之间是否以及如何变化将会很有趣.

    c++ language-lawyer c++11 c++14 c++17

    15
    推荐指数
    3
    解决办法
    1626
    查看次数

    带有指向非常量的指针和指向相同地址的常量参数的指针的函数调用

    我想编写一个函数,输入一个数据数组并使用指针输出另一个数据数组。

    我想知道如果两者都指向同一个地址会产生什么结果srcdst因为我知道编译器可以针对 const 进行优化。这是未定义的行为吗?(我标记了 C 和 C++,因为我不确定它们之间的答案是否不同,我想了解两者。)

    void f(const char *src, char *dst) {
        dst[2] = src[0];
        dst[1] = src[1];
        dst[0] = src[2];
    }
    
    int main() {
        char s[] = "123";
        f(s,s);
        printf("%s\n", s);
        return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    除了上面的问题,如果我删除const原来的代码,这个定义是否明确?

    c c++ constants undefined-behavior

    15
    推荐指数
    2
    解决办法
    549
    查看次数