【问题标题】:How to make the following code faster如何使以下代码更快
【发布时间】:2011-05-25 21:45:28
【问题描述】:
int u1, u2;  
unsigned long elm1[20], _mulpre[16][20], res1[40], res2[40]; 64 bits long     
res1, res2 initialized to zero.  

l = 60;  
while (l)  
{  
    for (i = 0; i < 20; i += 2)  
    {  
        u1 = (elm1[i] >> l) & 15;  
        u2 = (elm1[i + 1] >> l) & 15;

        for (k = 0; k < 20; k += 2)  
        {  
            simda = _mm_load_si128 ((__m128i *) &_mulpre[u1][k]);  
            simdb = _mm_load_si128 ((__m128i *) &res1[i + k]);  
            simdb = _mm_xor_si128  (simda, simdb);  
            _mm_store_si128 ((__m128i *)&res1[i + k], simdb);  

            simda = _mm_load_si128 ((__m128i *)&_mulpre[u2][k]);  
            simdb = _mm_load_si128 ((__m128i *)&res2[i + k]);  
            simdb = _mm_xor_si128  (simda, simdb);  
            _mm_store_si128 ((__m128i *)&res2[i + k], simdb);  
        } 
    }
    l -= 4;
    All res1, res2 values are left shifted by 4 bits.  
}

上面提到的代码在我的程序中被多次调用(profiler显示98%)。

编辑:在内部循环中, res1[i + k] 值会为相同的 (i + k) 值加载多次。我在 while 循环中尝试了这个,我将所有 res1 值加载到 simd 寄存器(数组)中,并在最里面的 for 循环中使用数组元素来更新数组元素。完成两个 for 循环后,我将数组值存储回 res1、re2。但是计算时间随之增加。知道我哪里错了吗?这个想法似乎是正确的

欢迎任何使其更快的建议。

【问题讨论】:

  • 这段代码应该做什么?您如何处理数据局部性问题(根据我的经验,这是优化中最重要的部分)?
  • 每次迭代res1,res2变量都会和_mulpre进行异或(移位(u1,u2)),然后res1,res2都左移4位
  • res1, res2, _mulpre都是局部变量,连续访问
  • 你实际测试过这段代码吗?只是看起来你正在尝试做错位和存储? [或者 unsigned long 在您的系统上可能是 64 位(在这种情况下,您可能希望使用 uint64_t 来代替,以避免混淆)。]
  • @anup: 好的 - 我建议你使用 中的类型以避免混淆。

标签: c optimization sse simd sse2


【解决方案1】:

不幸的是,最明显的优化可能已经由编译器完成了:

  • 你可以拉&amp;_mulpre[u1]&amp;mulpre[u2]我们的内循环。
  • 你可以拉&amp;res1[i]我们的内循环。
  • 对两个内部操作使用不同的变量并对其重新排序,可能会实现更好的流水线操作。

可能交换外部循环将改善elm1 上的缓存局部性。

【讨论】:

    【解决方案2】:

    好吧,你总是可以少调用它 :-)

    总输入和输出数据看起来相对较小,具体取决于您的设计和预期输入,仅缓存计算或进行惰性评估而不是预先计算可能是可行的。

    【讨论】:

    • 嗯,这个答案是关于查看函数的使用方式,看看是否有任何潜在的算法优化,而不是寻找神奇的指令。您的问题缺少一些信息来就此类更新提供好的建议,并且可能需要比您在这样的网站上找到的更多的努力和审查(好吧,除非您当然提供赏金)
    【解决方案3】:

    对于这样的例程,您几乎无能为力,因为加载和存储将是主要因素(对于单个计算指令,您正在执行 2 个加载 + 1 个存储 = 4 个总线周期)。

    【讨论】:

    • 有比我在代码中使用的更好的内存处理内在函数吗?
    • 不——你的带宽有限,没有灵丹妙药可以改变这一点(除了使用更好的算法和/或更快的硬件)。
    【解决方案4】:
    l = 60;  
    while (l)  
    {  
        for (i = 0; i < 20; i += 2)  
        {  
            u1 = (elm1[i] >> l) & 15;  
            u2 = (elm1[i + 1] >> l) & 15;
    
            for (k = 0; k < 20; k += 2)  
            {  
                _mm_stream_si128 ((__m128i *)&res1[i + k],
                        _mm_xor_si128  (
                                        _mm_load_si128 ((__m128i *) &_mulpre[u1][k]),
                                        _mm_load_si128 ((__m128i *) &res1[i + k]
                                       ));  
    
                mm_stream_si128 ((__m128i *)&res2[i + k],    
                        _mm_xor_si128  (
                                        _mm_load_si128 ((__m128i *)&_mulpre[u2][k]), 
                                        _mm_load_si128 ((__m128i *)&res2[i + k])
                                       ));  
            } 
        }
        l -= 4;
        All res1, res2 values are left shifted by 4 bits.  
    }
    
    1. 请记住您使用的是内在函数,使用较少的 _128mi/_mm128 值会加快您的程序。
    2. 试试 _mm_stream_si128(),它可能会加快存储过程。
    3. 尝试预取

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-21
      • 2020-01-13
      • 1970-01-01
      • 2020-05-29
      • 2015-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多