Melhorando um pouquinho a contagem de ciclos

No post sobre copia de blocos de memória (aqui) mostrei como media o tempo de execução de pedaços de um código em C usando a instrução RDTSC, disponível nos processadores Pentium ou superiores. Uma coisa que eu não disse é que essas instruções também tomam tempo de processamento (é claro!). Agora, vou mostrar como compensar esse tempo extra, que não faz parte de nossas medições… Basta medir “nada”:

#include <stdio.h>

__inline__ unsigned long long rdtsc(void)
{
  unsigned long long x;
  __asm__ __volatile__ ( "rdtsc" : "=A"(x) );
  return x;
}

void main(void)
{
  unsigned long long t;

  t = rdtsc();
  t = rdtsc() - t;

  printf("%lld ciclos \"perdidos\".\n", t);
}

Compile com a opção -O3 para a máxima otimização automática e execute o programa. Você obterá o valor 77. Isso significa que 77 ciclos são perdidos nas linhas marcadas em vermelho. O compilador gera algo do tipo:

/* Para a linha "t = rdtsc();": */
rdtsc
mov dword ptr [ebp+28],eax  /* EBP+28 é a posição da variável local 't' */
mov dword ptr [ebp+32],edx

/* Pra a linha "t = rdtsc() - t;": */
rdtsc
sub eax,dword ptr [ebp+28]
sbb edx,dword ptr [ebp+32]
mov dword ptr [ebp+28],eax
mov dword ptr [ebp+32],edx

Além de RDTSC tomar tempo, as movimentaçãoes para a variável local e as subtrações tomam tempo também… Assim, vou definir dois macros para podermos medir o tempo mais precisamente:

#define BEGIN_RDTSC(x) do { (x) = rdtsc() } while (0)
#define END_RDTSC(x) do { (x) = rdtsc() - (x) - 77; } while (0)

O artifício de usar do..while(0) nos macros serve a apenas um propósito: Se vocẽ esquecer de colocar um ponto-e-vírgula depois da “chamada” do macro, o compilador chiará! O do..while(0) é ignorado pelo compilador se tudo estiver certinho, já que while(0) significa nenhuma repetição!

Então, daqui para frente, teremos os arquivos rdtsc.c e rdtsc.h em nossos projetos, se quisermos medir ciclos. Primeiro, eis o rdtsc.h:

#ifndef __RDTSC_INCLUDED__
#define __RDTSC_INCLUDED__

#define BEGIN_RDTSC(x) do { (x) = rdtsc(); } while (0)
#define END_RDTSC(x) do { (x) = rdtsc() - (x) - 77; } while (0)

__inline__ unsigned long long rdtsc(void);

#endif /* __RDTSC_INCLUDED__ */

E, em segundo lugar, nosso arquivo rdtsc.c:

#include "rdtsc.h"

__inline__ unsigned long long rdtsc(void)
{
  unsigned long long x;
  __asm__ __volatile__ ( "rdtsc" : "=A"(x) );
  return x;
}
Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s