Uma nota sobre a “correção” no contador de ciclos…

O leitor “Pl4nkt0n” fez um excelente comentário no post anterior… De fato, a Intel recomenda a serialização antes e depois (duas vezes, no final) da medição via TSC:

unsigned long long c1H, c1L, c2H, c2L;
unsigned long long cycles;

__asm__ __volatile__ ( 
  "xor %%eax,%%eax\n"
  "cpuid\n"
  "rdtsc"
  : "=d" (c1H), "=a" (c1L) : : "%rbx", "%rcx" );

/* função a ser medida aqui */

__asm__ __volatile__ (
  "rdrscp\n"
  "movl %%edx,%0\n"
  "movl %%eax,%1\n"
  "cpuid\n"
: "=r" (c2H), "=r" (c2L) : : "%rax", "%rbx", "%rcx", "%rdx");

cycles = ((c2H << 32) + c2L) -
         ((c1H << 32) + c1L);

A função RDTSCP, assim como CPUID, serializa o processador (é um outro jeito de dizer que ele espera que todas as instruções ANTES dessas sejam executadas). Serializar, neste sentido, significa, “esperar até que todo mundo me alcance”.

Por que, então, escolhi inverter a ordem recomendada pela Intel? Note que no meu pequeno código em assembly, no post anterior, coloquei RDTSCP na função start_cycle_count() e uma simples RDTSC na end_cycle_count(). RDTSCP serializa o processador, RDTSC, não o faz. Assim, não forço nenhuma serialização da função de “término de contagem”! Fiz isso porque meus experimentos deram melhores resultados do que a recomendação (que pode ser lida aqui).

Em meus experimentos, mesmo obedecendo a recomedação, ainda assim, não obtenho valores precisos… Como falei antes, há muito mais coisas acontecendo por baixo dos panos que o simples processamento de instruções (branch predicion, cache misses, page misses, task switching, out-of-order executions, …). Tudo o que você poderá obter, com essa técnica, é uma idéia aproximada dos ciclos gastos por uma função ou código qualquer entre os blocos em assembly. Se quiser uma medição mais precisa, mas mesmo assim, “média”, use coisas como V-Tune ou perf.

Eis outro motivo para eu descartar o uso de CPUID. Eu não sei se essa instrução continuará serializando a CPU mesmo que o valor de EAX de entrada seja inválido! Note que nem mesmo o código de exemplo da Intel preocupa-se com isso… Mas eu sei que RDTSCP serializará, do mesmo jeito que CPUID faria, em todos os casos (a não ser que o kernel – ring 0 – tenha proibido!). Dai, cometi o exagero de chamar RDTSCP 16 vezes na função start_cycle_count() para ter absoluta certeza que a serialização seria obedecida (é provável que apenas uma, ou no máximo duas, chamadas à RDTSCP resolvesse!). E, como não temos mesmo precisão na medição, não vi motivos para serializar a CPU na saída…

A serialização na saída é importante? Talvez… Neste caso, talvez outra chamada à RDTSCP, ao invés de CPUID, ajudasse mais. É questão de testar…

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