C vs Go bucles y matemática simple

Cuando me cansé de programar en C, como muchos, estaba interesado en Go. Está estrictamente escrito, compilado, por lo tanto, es bastante productivo. Y luego quise averiguar cuán confundidos estaban los creadores de Go al optimizar su trabajo con bucles y números.

Para empezar, miramos cómo va C.

Escribimos un código tan simple:

#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; } 

Compilar con O2, desmontar:

 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> 

Obtenemos el tiempo de ejecución:

0m0,023s reales
usuario 0m0,019s
sys 0m0,004s

Parece que no hay ningún lugar para acelerar, pero tenemos un procesador moderno, para tales operaciones tenemos registros sse rápidos. Probamos las opciones gcc -mfpmath = sse -msse4.2 el resultado es el mismo.
Agregue -O3 y saludos:

  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> 

Se puede ver que se utilizan comandos SSE2 y registros SSE, y obtenemos un aumento de rendimiento triple:

0m0,006s reales
usuario 0m0,006s
sys 0m0,000s

También en Go:

 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> 


Los tiempos van:
ir regular:
0m0,021s reales
usuario 0m0,018s
sys 0m0,004s

gccgo:
0m0,058s reales
usuario 0m0,036s
sys 0m0,014s

El rendimiento, como en el caso de C y O2, también establece a gccgo el mismo resultado, pero funciona más tiempo que el compilador Go (1.10.4) normal. Aparentemente, debido al hecho de que el compilador regular optimiza perfectamente el lanzamiento de subprocesos (en mi caso, se crearon 5 subprocesos adicionales en 4 núcleos), la aplicación se ejecuta más rápido.

Conclusión



Todavía logré que el compilador Go estándar funcionara con instrucciones sse para el bucle deslizándolo nativo a sse float.

 package main // +build amd64 import "fmt" func main() { var i float64 = 0 var j float64 = 0 for i = 10000000; i>0; i-- { j += i } fmt.Println(j) } 


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>

Source: https://habr.com/ru/post/es432986/


All Articles