Ketika saya bosan pemrograman di C, seperti banyak orang, saya tertarik pada Go. Ini diketik ketat, dikompilasi, karena itu cukup produktif. Dan kemudian saya ingin mengetahui betapa bingungnya pencipta Go dalam mengoptimalkan pekerjaan mereka dengan loop dan angka.
Untuk memulainya, kita melihat bagaimana kinerja C.
Kami menulis kode sederhana seperti itu:
#include <stdint.h> #include <stdio.h> int main() { uint64_t i; uint64_t j = 0; for ( i = 10000000; i>0; i--) { j ^= i; } printf("%lu\n", j); return 0; }
Kompilasi dengan O2, bongkar:
564: 31 d2 xor %edx,%edx 566: b8 80 96 98 00 mov $0x989680,%eax 56b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 570: 48 31 c2 xor %rax,%rdx 573: 48 83 e8 01 sub $0x1,%rax 577: 75 f7 jne 570 <main+0x10>
Kami mendapatkan waktu eksekusi:
0m0,023s nyata
pengguna 0m0,019s
sys 0m0,004s
Tampaknya tidak ada tempat untuk berakselerasi, tetapi kami memiliki prosesor modern, untuk operasi seperti itu kami memiliki register yang cepat. Kami mencoba opsi gcc -mfpmath = sse -msse4.2 hasilnya sama.
Tambahkan -O3 dan sorakan:
57a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 580: 83 c0 01 add $0x1,%eax 583: 66 0f ef c8 pxor %xmm0,%xmm1 587: 66 0f d4 c2 paddq %xmm2,%xmm0 58b: 3d 40 4b 4c 00 cmp $0x4c4b40,%eax 590: 75 ee jne 580 <main+0x20>
Dapat dilihat bahwa perintah SSE2 dan register SSE digunakan, dan kami mendapatkan peningkatan kinerja tiga kali lipat:
0m0,006s nyata
pengguna 0m0,006s
sys 0m0,000s
Juga aktif:
package main import "fmt" func main() { i := 0 j := 0 for i = 10000000; i>0; i-- { j ^= i } fmt.Println(j) }
0x000000000048211a <+42>: lea -0x1(%rax),%rdx 0x000000000048211e <+46>: xor %rax,%rcx 0x0000000000482121 <+49>: mov %rdx,%rax 0x0000000000482124 <+52>: test %rax,%rax 0x0000000000482127 <+55>: ja 0x48211a <main.main+42>
Pengaturan Waktu:
go reguler:
0m0,021s nyata
pengguna 0m0,018s
sys 0m0,004s
gccgo:
0m0,058s nyata
pengguna 0m0,036s
sys 0m0,014s
Kinerja, seperti dalam kasus C dan O2, juga menetapkan gccgo hasil yang sama, tetapi bekerja lebih lama daripada kompiler Go (1.10.4) biasa. Rupanya karena fakta bahwa kompiler biasa dengan sempurna mengoptimalkan peluncuran utas (dalam kasus saya, 5 utas tambahan dibuat pada 4 inti), aplikasi berjalan lebih cepat.
Kesimpulan
Saya masih berhasil mendapatkan kompiler Go standar untuk bekerja dengan instruksi sse untuk loop dengan menyelipkannya asli ke sse float.
package main
0x0000000000484bbe <+46>: movsd 0x4252a(%rip),%xmm3 # 0x4c70f0 <$f64.3ff0000000000000>
0x0000000000484bc6 <+54>: movups %xmm0,%xmm4
0x0000000000484bc9 <+57>: subsd %xmm3,%xmm0
0x0000000000484bcd <+61>: addsd %xmm4,%xmm1
0x0000000000484bd1 <+65>: xorps %xmm2,%xmm2
0x0000000000484bd4 <+68>: ucomisd %xmm2,%xmm0
0x0000000000484bd8 <+72>: ja 0x484bbe <main.main+46>