Melhorando um pouquinho mais a contagem de ciclos

A long time ago, in a blog near, very near you… Eu postei 2 artigos (este e este) mostrando como contar ciclos nos seus códigos em C usando a instrução RDTSC. Acontece que a medição não tem como ser exata, já que existem muitos “ruídos” envolvidos (interrupções que só podem ser desabilitadas no nível do kernel, task switching, etc).

Além disso, tivemos o problema de códigos otimizados rearranjando as chamadas à função rdtsc(). E, ainda por cima, temos o problema do código não funcionar com C++…

Espero resolver esses problemas agora. Eis os 2 arquivos (rdtsc.h e rdtsc.c, respectivamente):

/* rdtsc.h */
#ifndef __RDTSC_INCLUDED__
#define __RDTSC_INCLUDED__

#include <stdint.h>

/* Isso faz com que a rotina funcione em C++ */
#ifdef __cplusplus
extern "C" {
#endif

/* Entre 2 chamadas a rdtsc(), ocorrem 308 ciclos. */
#define RDTSC_FUNC_DELAY 308ULL

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

__volatile__ uint64_t rdtsc(void);

#ifdef __cplusplus
}
#endif

#endif /* __RDTSC_INCLUDED__ */

E agora, rdtsc.c

/* rdtsc.c */
#include "rdtsc.h"

#ifdef __cplusplus
extern "C" {
#endif

__volatile__ uint64_t rdtsc(void)
{
  uint64_t x;
  __asm__ __volatile__ ( "xorl %%eax, %%eax;"
                         "pushl %%ebx;"
                         "pushl %%ecx;"
                         "cpuid;"
                         "popl %%ecx;"
                         "popl %%ebx;"
                         "rdtsc" :
                         "=A"(x) );
  return x;
}

#ifdef __cplusplus
}
#endif

Ahhhh, agora temos um monte de instruções em assembly em rdtsc()!

Para tentar melhorar a contagem, coloquei uma instrução CPUID antes de RDTSC. A instrução CPUID serializa o processador (limpa a fila de pré-busca), mais ou menos fazendo com que a rotina seja chamada “do zero”. Repare que salvei e recuperei os registradores ECX e EBX. Isso poderia ter sido feito modificando o bloco __asm__, adicionando registradores clobbered (o que me lembra do Coisa, do Quarteto Fantástico: “It’s clobber time!”). Os registradores clobbered são aqueles que não podem ser modificados. A coisa poderia ter ficado assim:

  __asm__ __volatile__ ( "xorl %%eax, %%eax;"
                         "cpuid;"
                         "rdtsc" :
                         "=A"(x) :    /* output */
                         :            /* input */
                         "ebx", "ecx" /* clobbered */
                         );

O que é, essencialmente, a mesma coisa. Optei pela rotina anterior porque quero um tempo constante entre CPUID e RDTSC. No caso do uso de registrador clobbered eles podem ou não serem salvos, de acordo com a decisão de otimização do compilador. Fazendo de modo explícito eu garanto que o tempo será constante já que EBX e ECX sempre serão salvos e recuperados, na rotina.

Note que mudei o atributo da função rdtsc() de __inline__ para __volatile__. Isso porque uma função marcada como volátil é “ignorada” para fins de otimização. É o equivalente a dizer ao compilador: “Nem pense em encostar a mão no que eu, o programador, fiz aqui, seu bostinha!”.

Agora… como é que eu calculei o valor de RDTSC_FUNC_DELAY? Chamando a função rdtsc() duas vezes, ora bolas!

#include <stdio.h>
#include "rdtsc.h"

int main(void)
{
  uint64_t t;

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

  printf("%llu\n", t);

  return 0;
}

Pegue o valor retornado (ou, pelo menos, o menor valor que você obtiver depois de executar o programinha) e atualize o símbolo RDTSC_FUNC_DELAY, em rdtsc.h.

A rotina ainda será imprecisa, mas é menos do que a anterior.

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