Haskell und C ++ wurden verglichen und Sprung und cmov wurden verglichen

Das Lustige ist - <br> Ich habe den Haskell-Code über das LLVM-Backend gesammelt, <br> aber gleichzeitig habe ich ihn mit GCC verglichen


In dem Artikel [Link] wurde angegeben, dass die Leistung von Haskell-Code C ++ - Code überlegen war. Was sofort Interesse weckte, als Beide können vom Compiler von LLVM generiert werden. Dies bedeutet, dass entweder Eskell dem Compiler weitere Hinweise geben kann oder dass etwas mit der C ++ - Implementierung nicht stimmt. Als nächstes werden wir analysieren, wie eine Reihe von Unfällen in den Handlungen des Autors zu falschen Schlussfolgerungen führte, die in der folgenden Tabelle (unter dem Schnitt) beschrieben sind.


Vorwort


Kürzlich ein weiterer Artikel aus0xd34df00dHaskell-Code-Optimierung. In solchen Fällen wird es natürlich mit dem unbestreitbaren Leistungsführer C / C ++ verglichen. Dann kam eine Analyse dieses Artikels ausyleodarüber, welcher asm-Code wirklich besser ist und was der Unterschied zwischen den Implementierungen in verschiedenen Sprachen ist (ich empfehle das Lesen). Noch früher (vor ungefähr anderthalb Monaten) wurde der vorherige Artikel aus der Reihe Haskell vs C / C ++ veröffentlicht, und ich habe eine ähnliche Analyse durchgeführt, aber anstatt ihn leider bei Habr zu veröffentlichen, habe ich ihn in die Backbox gelegt. Die hitzigen Diskussionen in den Kommentaren dieser Woche veranlassten mich, zum vorherigen Thema zurückzukehren. Heute habe ich endlich das Abschriften-Dokument aus der Schublade geholt, den Staub abgestaubt,fertigund geben Sie es für Ihre Überprüfung.


Einführung


, [], :



.
clang 9103%
gcc 9.2125%
C++ gcc 9.2163%
C++ clang 9323%

/++, .. , , " , ". "", . , , , 10- .



-, , ++ , , , . , zero-cost, , , , - ++, . , clang 3 (!) , +, , .. lang llvm — .



, : gcc clang. , . ( ), gcc . , , , , , .




std::min({...})


, , std::min.


C++.
, std::min({delCost, insCost, substCost})
std::min(substCost, std::min(delCost, insCost)),
clang — 0.840
, .
( — 0xd34df00d)

:


A.unsafeWrite v1 (j + 1) $ min (substCost + substCostBase) $ 1 + min delCost insCost

, min ! (, ). , , C++ , , llvm llvm. , . , , "skell " . , " ++; , PHP" , , . :


(1)
haskell/llvm910ms-
gcc 9.21211ms1211ms
clang 91915ms852ms

stdlib gcc - std::min , ++. std::min_element. , , , :


f(int, int, int):
        cmp     esi, edi
        mov     eax, edx
        cmovg   esi, edi
        cmp     esi, edx
        cmovle  eax, esi
        ret

: cmov* = conditional move (: g — greater, le — less equal, ..).


, , , , clang, gcc, - ( rsp ):


fptr(int*, int*, int*):
        mov     eax, dword ptr [rdi]
        mov     dword ptr [rsp - 12], eax
        mov     ecx, dword ptr [rsi]
        mov     dword ptr [rsp - 8], ecx
        cmp     ecx, eax
        cmovle  eax, ecx
        mov     ecx, dword ptr [rdx]
        cmp     ecx, eax
        cmovle  eax, ecx
        ret

, clang . initializer_list asm -O1, (-O2), asm . , std::min(std::initializer_list) -, -, , .


s1[i]


, — , ++.


s1[i] ! ()

, , , - , , . , s1[i] ,


  let s1char = s1 `BS.index` i
  let go j | j == n = pure ()
    --  

++, , clang. .. + llvm - , -march=native. , , std::min , , ! , - , " " , .


(2)(3)
haskell/llvm910ms--
gcc 9.21211ms1195ms1195ms
clang 91915ms742ms831ms


C++ .
,
C main', .
( )

, , , - , :


  size_t lev_dist(const std::string &s1, const std::string &s2) {
    const auto m = s1.size();
    const auto n = s2.size();

    std::vector<int> v0;
    v0.resize(n + 1);
    std::iota(v0.begin(), v0.end(), 0);
    auto v1 = v0;

    for (size_t i = 0; i < m; ++i) {
      v1[0] = i + 1;
      char c1 = s1[i];
      for (size_t j = 0; j < n; ++j) {
        auto substCost = c1 == s2[j] ? v0[j] : (v0[j] + 1);
        v1[j + 1] = std::min(substCost, std::min(v0[j + 1], v1[j]) + 1);
      }
      std::swap(v0, v1);
    }
    return v0[n];
  }

32- int — , ( ).


(3b)
haskell/llvm910ms-
gcc 9.21210ms831ms
clang 91915ms741ms

, GCC . j , GCC. clang .



LLVM == LLVM


-, , ++ Haskell , clang-9. , Skylake C++ . , , , Haswell, .


, , , GCC LLVM.


, , llvm, ffi, .


GCC vs CLANG


-, , gcc . , clang- .


, , , , (#if !defined(__GNUC__) || defined(__llvm__)), ++ , , ++ .


clang ( ) . ( )

, GCC LLVM. , asm. gcc - : , cmov* min ( , ). (3), , , ++ :


      for (size_t j = 0; j < n; ++j) {
        auto delCost = v0[j + 1] + 1;
        auto insCost = v1[j] + 1;
        auto substCost = c1 == s2[j] ? v0[j] : (v0[j] + 1);
        v1[j + 1] = std::min(substCost, std::min(delCost, insCost));
      }

, , :


.L42:
        inc     rcx  // j++
        mov     rdi, QWORD PTR [r12+rcx*8]  //  v0[j+1]
        xor     edx, edx  //  %edx
        cmp     r10b, BYTE PTR [r11-1+rcx]  // c1 == s2[j]
        setne   dl  //     %rdx
        lea     r9, [rdi+1]  //  v0[j+1] + 1
        add     rdx, QWORD PTR [r12-8+rcx*8]  //  v0[j]
        lea     rsi, [rax+1]  // %rax  v1[j]
        cmp     rdi, rax  //  v0[j+1]  v1[j]  += 1
        mov     rax, r9
        cmovg   rax, rsi  //       += 1
        cmp     rax, rdx  //  %rax, %rdx
        cmovg   rax, rdx
        mov     QWORD PTR [r8+rcx*8], rax  // v1[j+1] = ...
        cmp     rbx, rcx  // loop
        jne     .L42

, — v1[j] %rax.


LLVM, - , . , , :


.LBB1_40:                               #   in Loop: Header=BB1_36 Depth=2
        mov     qword ptr [r14 + 8*rsi + 8], rax
        mov     rdx, qword ptr [rbx + 8*rsi + 16]
        inc     rdx
        inc     rax
        xor     ebp, ebp
        cmp     cl, byte ptr [r13 + rsi + 1]
        setne   bpl
        add     rbp, qword ptr [rbx + 8*rsi + 8]
        cmp     rax, rdx
        jg      .LBB1_41
        lea     rdi, [rsi + 2]
        cmp     rax, rbp
        jle     .LBB1_44
        jmp     .LBB1_43

: jmp, j* = jump (: jg — greater, jle — less equal, ..).


v0[j+1], v0[j], cmp s1[i], cmp + jump . , , ( , ) , , . — .


str a — str a, str a — str brandom-random x2
gcc 9.21190 ms1190 ms
clang 9837 ms1662 ms

, GCC , LLVM 2 (!) , .


, . : (jump), — (cmov).
, — .


, , . , , PGO (, JIT ). , GCC PGO clang. — , . , , // , , , .



  • , —
  • , LLVM
  • , GCC LLVM ,
  • ffi. , .. , , ,

, , .



: , , "" , , . Rust (, ).


P.S.


— , -
( )

, — , .. , , , s1 s2. . -, O(n*m) ( for). , . , k. , . , .


LinearLeopard .




  • : -O3 -march=native -std=gnu++17.
  • Prozessor: Intel i5-8250U (ja, Laptop)
  • Betriebssystem: Ubuntu 19.04 / Linux 5.0.0
  • Der erste Lauf, um den Turbo-Boost zu beschleunigen, dann mindestens fünf in Folge. Abweichungen innerhalb von 1-2%.
  • Zwischen den Starts verschiedener Implementierungen von 1s werden wir uns abkühlen (ja, Laptop).

Hinzugefügt: Skripte für die Faulen


Sie können dasselbe auf Ihrer Hardware ausführen und der Öffentlichkeit das Ergebnis mitteilen: einen Link zum Github .


Hinzugefügt: Ergebnisse ohne -march = native


Gemäß den Anfragen in den Kommentaren habe ich beschlossen, den Einfluss dieser Flagge zu überprüfen.


Flaggen-O3 -march = native-O3 -march = native-O3-O3
CompilerOriginalDotiert (3b)OriginalDotiert (3b)
haskell / llvm- -- -910 ms- -
gcc 9.21210 ms831 ms1191ms791 ms
klirren 91915ms741 ms1924ms807ms

All Articles