Haskell dan C ++ dibandingkan, dan melompat dan cmov dibandingkan

Yang lucu adalah - Situs saya mengumpulkan kode Haskell melalui backend LLVM, Situs tetapi pada saat yang sama saya membandingkannya dengan GCC


Artikel [tautan] menyatakan bahwa kinerja kode Haskell lebih unggul daripada kode C ++. Apa yang segera membangkitkan minat, seperti keduanya dapat dihasilkan oleh kompiler LLVM, yang berarti Eskell dapat memberikan lebih banyak petunjuk kepada kompiler, atau ada sesuatu yang salah dengan implementasi C ++. Selanjutnya, kita akan menganalisis bagaimana serangkaian kecelakaan dalam tindakan penulis menyebabkan kesimpulan yang salah, yang dijelaskan dalam tabel di bawah ini (di bawah potongan).


Kata pengantar


Baru-baru ini, artikel lain dari0xd34df00dOptimasi kode Haskell. Ini dibandingkan dalam kasus seperti itu secara alami dengan pemimpin yang tidak dapat disangkal dalam kinerja - C / C ++. Kemudian datang parsing artikel ini dariyleotentang apa kode asm benar-benar lebih baik, dan apa perbedaan antara implementasi pada bahasa yang berbeda (saya sarankan membaca). Bahkan lebih awal (sekitar satu setengah bulan yang lalu), artikel sebelumnya dari seri Haskell vs C / C ++ diterbitkan, dan saya melakukan analisis yang serupa, tetapi alih-alih menerbitkannya ke Habr - sayangnya, saya meletakkannya di kotak belakang. Diskusi hangat dalam komentar minggu ini mendorong saya untuk kembali ke topik sebelumnya. Hari ini saya akhirnya mengeluarkan dokumen penurunan harga dari laci, membersihkan debu,jadi, dan berikan untuk ulasan Anda.


pengantar


, [], :



.
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.
  • Prosesor: Intel i5-8250U (ya, laptop)
  • OS: Ubuntu 19.04 / Linux 5.0.0
  • Lari pertama untuk mempercepat turbo boost, lalu ambil minimal lima berturut-turut. Penyimpangan dalam 1-2%.
  • Di antara peluncuran implementasi 1s yang berbeda, kami akan tenang (ya, laptop)

Ditambahkan: skrip untuk yang malas


Anda dapat menjalankan hal yang sama pada perangkat keras Anda dan memberi tahu publik hasilnya: tautan ke github .


Ditambahkan: Hasil tanpa -march = asli


Menurut permintaan di komentar, saya memutuskan untuk memeriksa pengaruh bendera ini.


Bendera-O3 -march = asli-O3 -march = asli-O3-O3
PenyusunAsliDoped (3b)AsliDoped (3b)
haskell / llvm--910 ms-
gcc 9.21210 ms831 ms1191ms791 ms
dentang 91915 ms741 ms1924 ms807 ms

All Articles