2013-04-22 10 views
6

Ich versuche diesen Code zu optimieren.Hotspot in einer for-Schleife

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 zeigt, dass etwa die Hälfte der Prozessorzeit in der zweiten forAnweisung ausgegeben wird, nicht die 2 Zeilen innerhalb derforSchleife. Wenn ich die Montage Quelle entrollen, kann ich sehen, dass die for C++ Anweisung in eine Anzahl von OP-Codes übersetzt worden ist, von denen 3 scheinen die CPU-Zeit zu verschlingen:

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 <------------------- 

Ich verstehe nicht, wie man ein einfaches bewegen und vergleichen könnte das zeitaufwendig sein.

+0

@Agentlien Es ist nicht möglich, x86-64-Code auf einer 32-Bit-CPU auszuführen. 64-Bit Register 'rax',' rbx', 'rcx',' rdx', 'rsi',' rdi', 'r8',' r9', 'r10',' r11', 'r12',' r13 ',' r14', 'r15',' rbp' und 'rsp' sind nur in x86-64 verfügbar, nicht in 32-Bit x86-Code. – nrz

+1

Ich sehe nichts ungewöhnliches in diesen Ergebnissen. Profiler kann Ihnen nicht die zeitaufwändigsten Anweisungen zeigen (weil alle modernen CPUs eine Out-of-Order- und spekulative Ausführung verwenden). Wie erwartet, sind die zeitaufwendigsten Anweisungen 'cmovbe' (Implementieren von' std :: min'). Sie können die größten Zeiten in ihrer Nähe sehen: 460,772ms und 558,298ms. cmovbe sind die zeitaufwendigsten Befehle, da sie normalerweise eine große Latenz und mehr Abhängigkeiten von vorhergehenden Anweisungen haben. –

+1

@EvgenyKluev vielleicht ein wenig offtopic, aber wissen Sie, ob Valgrind genauere Messungen liefern würde? – NoSenseEtAl

Antwort

8

Der Profiler kann Ihnen nicht die zeitaufwendigsten Anweisungen zeigen, da alle modernen CPUs eine Out-of-Order- und spekulative Ausführung verwenden. Es ist nicht ungewöhnlich, die größte gemessene Zeit ein oder zwei Zeilen von der zeitaufwendigsten Anweisung zu sehen.

Wie erwartet, sind die zeitaufwendigsten Anweisungen hier cmovbe (Implementierung std::min). Sie können die größten Zeiten in ihrer Nähe sehen: 460,772ms und 558,298ms. cmovbe sind die zeitaufwendigsten Anweisungen, da sie normalerweise eine große Latenz und mehr Abhängigkeiten von vorhergehenden Anweisungen haben.