【问题标题】:Hotspot in a for loopfor循环中的热点
【发布时间】:2013-04-15 05:34:06
【问题描述】:

我正在尝试优化这段代码。

static
lvh_distance levenshtein_distance( const std::string & s1, const std::string & s2 )
{
    const size_t len1 = s1.size(), len2 = s2.size();
    std::vector<unsigned int> col( len2+1 ), prevCol( len2+1 );

    const size_t prevColSize = prevCol.size();
    for( unsigned int i = 0; i < prevColSize; i++ )
        prevCol[i] = i;

    for( unsigned int i = 0, j; i < len1; ++i )
    {
        col[0] = i+1;
        const char s1i = s1[i];
        for( j = 0; j < len2; ++j )
        {
            const auto minPrev = 1 + std::min( col[j], prevCol[1 + j] );
            col[j+1] = std::min( minPrev, prevCol[j] + (  s1i == s2[j] ? 0 : 1 ) );

        }
        col.swap( prevCol );
    }
    return prevCol[len2];
}

Intel VTune 显示大约一半的处理器时间花在第二个for 指令上,而不是for 循环内的2 行 。当我展开汇编源代码时,我可以看到for c++ 指令已被翻译成多个操作码,其中3个似乎正在吞噬CPU时间:

Code Location   Source Line Assembly    CPU Time
        Block 14:   [Unknown]
0x420c00    31  movq  (%r12), %rcx  19.969ms
0x420c04    30  add $0x1, %r11d [Unknown]
0x420c08    32  test %rbx, %rbx [Unknown]
0x420c0b    30  movl  %r11d, (%r8)  [Unknown]
0x420c0e    31  movzxb  (%rcx,%rdx,1), %r9d 19.964ms
0x420c13    32  jz 0x420c53 <Block 17>  [Unknown]
        Block 15:   [Unknown]
0x420c15    32  movq  (%rbp), %r10  [Unknown]
0x420c19    32  mov %r11d, %edx [Unknown]
0x420c1c    32  xor %ecx, %ecx  39.928ms
0x420c1e    32  xor %edi, %edi  [Unknown]
        Block 16:   [Unknown]
0x420c20    34  add $0x1, %edi  29.994ms
0x420c23    34  mov %edi, %esi  30.956ms
0x420c25    34  movl  (%rax,%rsi,4), %r15d  180.659ms
0x420c29    34  cmp %r15d, %edx 39.896ms
0x420c2c    34  cmovbe %edx, %r15d  19.951ms
0x420c30    35  xor %edx, %edx  460.772ms
0x420c32    34  add $0x1, %r15d 19.946ms
0x420c36    35  cmpb  (%r10,%rcx,1), %r9b   169.659ms  
0x420c3a    35  setnz %dl   49.815ms
0x420c3d    35  addl  (%rax,%rcx,4), %edx   [Unknown]
0x420c40    32  mov %rsi, %rcx               210.615ms  <------------------
0x420c43    32  cmp %edx, %r15d              29.936ms
0x420c46    32  cmovbe %r15d, %edx          29.938ms
0x420c4a    32  cmp %rsi, %rbx              558.298ms  <-------------------
0x420c4d    35  movl  %edx, (%r8,%rsi,4)    19.965ms
0x420c51    32  jnbe 0x420c20 <Block 16>    200.625ms  <-------------------

我不明白一个简单的移动和比较怎么会那么耗时。

【问题讨论】:

  • @Agentlien 在 32 位 CPU 上执行这样的 x86-64 代码是不可能的。 64位寄存器rax,@,rdx,@,rsir8r9r10r12r12r14r15rbprsp 仅适用于 x86-64,不适用于 32 位 x86 代码。
  • 我在这些结果中没有发现任何异常。 Profiler 无法向您显示确切的最耗时的指令(因为所有现代 CPU 都使用乱序和推测执行)。正如预期的那样,最耗时的指令是cmovbe(实现std::min)。您可以看到它们附近的最大时间:460.772ms 和 558.298ms。 cmovbe 是最耗时的指令,因为它们通常具有较大的延迟并且对前面的指令有更多的依赖。
  • @EvgenyKluev 可能有点离题,但你知道 valgrind 是否会提供更精确的测量结果吗?
  • @NoSenseEtAl:我不知道 valgrind 在这里是否更精确。我认为这不太可能。

标签: c++ optimization assembly profiling intel-vtune


【解决方案1】:

Profiler 无法准确显示最耗时的指令,因为所有现代 CPU 都使用乱序和推测执行。距离最耗时的指令一两行的最大测量时间并不罕见。

不出所料,这里最耗时的指令是cmovbe(实现std::min)。您可以看到它们附近的最大时间:460.772ms 和 558.298ms。 cmovbe 是最耗时的指令,因为它们通常具有较大的延迟并且对前面的指令有更多的依赖。

【讨论】:

    猜你喜欢
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-18
    • 2021-07-30
    相关资源
    最近更新 更多