比较了Haskell和C ++,比较了jump和cmov

有趣的是-<br>我通过LLVM后端收集了Haskell代码,<br>但同时我将其与GCC进行了比较


[link]文章指出,Haskell代码的性能优于C ++代码。立刻引起了人们的兴趣 两者都可以由LLVM由编译器生成,这意味着Eskell可以为编译器提供更多提示,或者C ++实现有问题。接下来,我们将分析作者的行为中发生的一系列事故如何导致错误的结论,下表中对此进行了描述(下划线)。


前言


最近,另一篇文章来自0xd34df00dHaskell代码优化。在这种情况下,自然可以将其与不可否认的性能领先者-C / C ++进行比较。这时来了解析本文来自音阶关于哪种asm代码真正更好,以及在不同语言上的实现之间有什么区别(我建议阅读)。甚至更早(大约一个半月前),Haskell与C / C ++系列的前一篇文章也已发布,我进行了类似的分析,但是没有将其发布到Habr-,,我将其放在了后箱中。本周评论中的激烈讨论促使我回到上一个主题。今天我终于把那笔降价文件从抽屉里拿出来,除掉了灰尘,完了,并提供您的评论。


介绍


, [], :



.
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.
  • 处理器:Intel i5-8250U(是,笔记本电脑)
  • 作业系统:Ubuntu 19.04 / Linux 5.0.0
  • 首先运行以加速涡轮增压,然后至少连续行驶五次。偏差在1-2%以内。
  • 在推出不同的1s实施方案之间,我们会降温(是的,笔记本电脑)

补充:懒惰脚本


您可以在硬件上运行相同的东西,并将结果告知公众:指向github链接


补充:没有-march = native的结果


根据评论中的请求,我决定检查此标志的影响。


标志-O3 -march =本机-O3 -march =本机-O3-O3
编译器原版的掺杂的(3b)原版的掺杂的(3b)
哈斯克尔/ llvm----910毫秒--
gcc 9.21210毫秒831毫秒1191毫秒791毫秒
lang 91915毫秒741毫秒1924毫秒807毫秒

All Articles