Hot Posts

6/recent/ticker-posts

n ++ é mais rápido do que n = n + 1?


Você já olhou para a assembléia para ver se há alguma diferença?
Aqui está um pouco de código C.
  1. int binary(int i){
  2. i = i + 1;
  3. return i;
  4. }
  5.  
  6. int unary(int i){
  7. i++;
  8. return i;
  9. }
Aqui está o compilador que estou usando.
  1. rountree4@cuyahoga ~$ clang --version
  2. Apple LLVM version 7.0.0 (clang-700.1.76)
  3. Target: x86_64-apple-darwin15.6.0
  4. Thread model: posix
Veja como gerar o assembly sem compilá-lo.
  1. rountree4@cuyahoga ~$ clang -S -O3 foo.c
E aqui está o código de montagem para unary.
  1. _binary: ## @binary
  2. .cfi_startproc
  3. ## BB#0:
  4. pushq %rbp
  5. Ltmp0:
  6. .cfi_def_cfa_offset 16
  7. Ltmp1:
  8. .cfi_offset %rbp, -16
  9. movq %rsp, %rbp
  10. Ltmp2:
  11. .cfi_def_cfa_register %rbp
  12. ## kill: EDI<def> EDI<kill> RDI<def>
  13. leal 1(%rdi), %eax
  14. popq %rbp
  15. retq
  16. .cfi_endproc
O código gerado é idêntico e, portanto, o desempenho será idêntico (todos os outros parâmetros sendo iguais).
  1. _unary: ## @unary
  2. .cfi_startproc
  3. ## BB#0:
  4. pushq %rbp
  5. Ltmp3:
  6. .cfi_def_cfa_offset 16
  7. Ltmp4:
  8. .cfi_offset %rbp, -16
  9. movq %rsp, %rbp
  10. Ltmp5:
  11. .cfi_def_cfa_register %rbp
  12. ## kill: EDI<def> EDI<kill> RDI<def>
  13. leal 1(%rdi), %eax
  14. popq %rbp
  15. retq
  16. .cfi_endproc
O código gerado é idêntico e, portanto, o desempenho será idêntico (todos os outros parâmetros sendo iguais).

  1. Admito que uma seqüência de instruções que requer uma instrução separada para carregar um valor imediato pode ser mais lenta do que uma que não requer uma carga separada. As leainstruções acima nãorequerem uma carga separada para o valor imediato. O valor imediato é codificado nas instruções.
  2. Dizendo-me que eu tenho uma regressão de desempenho não é grande coisa. Contar com as pessoas do LLVM que eles têm uma regressão de desempenho é um negócio muito maior. Dizer que eles têm uma regressão de desempenho em um dos caminhos de código mais comuns é algo que eu assumiria apenas com muita cautela. Dizendo-lhes que feriram seu desempenho e não fornecem dados para demonstrar que é uma boa maneira de não ser levado a sério.
Então, como você pode fazer a apresentação de um bug de desempenho com o LLVM?
Primeiro, vejamos se a Intel faz as coisas de maneira diferente.
  1. rountree@quartz386 ~/Quora$ icc --version
  2. icc_orig (ICC) 16.0.3 20160415
  3. Copyright (C) 1985-2016 Intel Corporation. All rights reserved.
Aqui está a assembléia para unário:
  1. # -- Begin unary
  2. .text
  3. # mark_begin;
  4. .align 16,0x90
  5. .globl unary
  6. # --- unary(int)
  7. unary:
  8. # parameter 1: %edi
  9. ..B2.1: # Preds ..B2.0
  10. .cfi_startproc
  11. ..___tag_value_unary.4:
  12. ..L5:
  13. #6.17
  14. incl %edi #7.2
  15. movl %edi, %eax #8.9
  16. ret #8.9
  17. .align 16,0x90
  18. .cfi_endproc
  19. # LOE
  20. # mark_end;
  21. .type unary,@function
  22. .size unary,.-unary
  23. .data
  24. # -- End unary
E aqui está a montagem para o binário, exceto que esta vez estou usando .n=n+7
  1. # -- Begin binary
  2. .text
  3. # mark_begin;
  4. .align 16,0x90
  5. .globl binary
  6. # --- binary(int)
  7. binary:
  8. # parameter 1: %edi
  9. ..B1.1: # Preds ..B1.0
  10. .cfi_startproc
  11. ..___tag_value_binary.1:
  12. ..L2:
  13. #1.18
  14. addl $7, %edi #2.10
  15. movl %edi, %eax #3.9
  16. ret #3.9
  17. .align 16,0x90
  18. .cfi_endproc
  19. # LOE
  20. # mark_end;
  21. .type binary,@function
  22. .size binary,.-binary
  23. .data
  24. # -- End binary
Talvez, surpreendentemente, usar as instruções addlou inclrequer um adicional movlpara obter o resultado eax. Esse movimento é dependente do resultado da instrução aritmética anterior; os dois não podem ser executados em paralelo.
Então, o que é mais rápido?
Aqui está o equipamento de teste.
  1. #include <stdio.h>
  2. #include <sys/time.h>
  3. #include <stdint.h>
  4.  
  5. int binary(int i){
  6. i = i + 7;
  7. return i;
  8. }
  9.  
  10. int unary(int i){
  11. i++;
  12. return i;
  13. }
  14.  
  15. int a;
  16. volatile uint64_t i, j;
  17.  
  18. int main(){
  19. struct timeval start, end;
  20.  
  21. gettimeofday(&start, NULL);
  22. for(i = 0; i<100; i++){
  23. for (j=0; j<1000000; j++){
  24. a = binary(a);
  25. }
  26. }
  27. gettimeofday(&end, NULL);
  28.  
  29. fprintf(stdout, "Binary time in seconds: %lf\n",
  30. (end.tv_sec - start.tv_sec) +
  31. (end.tv_usec - start.tv_usec)/1000000.0);
  32.  
  33. gettimeofday(&start, NULL);
  34. for(i = 0; i<100; i++){
  35. for (j=0; j<1000000; j++){
  36. a = unary(a);
  37. }
  38. }
  39. gettimeofday(&end, NULL);
  40.  
  41. fprintf(stdout, "Unary time in seconds: %lf\n",
  42. (end.tv_sec - start.tv_sec) +
  43. (end.tv_usec - start.tv_usec)/1000000.0);
  44.  
  45. gettimeofday(&start, NULL);
  46. for(i = 0; i<100; i++){
  47. for (j=0; j<1000000; j++){
  48. }
  49. }
  50. gettimeofday(&end, NULL);
  51.  
  52. fprintf(stdout, "Loop time in seconds: %lf\n",
  53. (end.tv_sec - start.tv_sec) +
  54. (end.tv_usec - start.tv_usec)/1000000.0);
  55.  
  56. return !a;
  57. }
Aqui estão os resultados para o compilador Intel icc.
  1. rountree@quartz386 ~/Quora$ ./icc.out
  2. Binary time in seconds: 0.270814
  3. Unary time in seconds: 0.270599
  4. Loop time in seconds: 0.231791
E aqui estão os resultados do gcc 4.9.3, pois não tenho clang e icc na mesma máquina, mas gcc também usa código idêntico baseado em lea para ambas as funções.
  1. rountree@quartz386 ~/Quora$ ./gcc.out
  2. Binary time in seconds: 0.232028
  3. Unary time in seconds: 0.231783
  4. Loop time in seconds: 0.231703
O tempo de loop está próximo o suficiente para idêntico. Em loop + tempo de função, porém, o leacódigo é significativamente mais rápido. Quanto?
  1. Function time (seconds)
  2. binary unary
  3. gcc 0.000245 0.000080
  4. intel 0.039023 0.038808
Nessa instância particular, reivindicar incfoi mais rápido do que leaerrado por duas ordens de grandeza.
Estou ansioso para dar a Intel um tempo difícil sobre isso quando eu estiver visitando eles em Hillsboro na próxima semana.

Postar um comentário

0 Comentários