
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
, [], :
/++, .. , , " , ". "", . , , , 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" , , . :
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
, , ! , - , " " , .
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
β , ( ).
, 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 . , , ( , ) , , . β .
, 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.