Jump to content

Medindo ciclos de clock também no ARM


fredericopissarra

Recommended Posts

Posted

Costumo usar essas "funçõeszinhas" para medir a quantidade de ciclos de clock "gastos" por uma função. Acrescentei o suporte ao AArch64.
O AArch32 também tem, mas é mais complicado. Note que o PMC (Performance Monitoring Counter) usado no AArch64 é para o privilégio 0 (user).

PS: Esse troço funciona bem com processadores das famílias Ivy Bridge em diante. Não sei quanto aos anteriores... E, no caso do ARM, para o Cortex A-52 em diante é certo...

#ifndef CYCLE_COUNTING_INCLUDED__
#define CYCLE_COUNTING_INCLUDED__

#ifndef __GNUC__
# error Works only on GCC
#endif

/* ==========================================
    Quick & Dirty cycle counting...

    As funções usadas para contar a quantidade de ciclos
    de clock gastos num bloco de código.

    Exemplo de uso:

      uint64_t t;

      BEGIN_TSC();      
      f();
      t = END_TSC();

    É conveniente compilar o código sob teste com a
    opção -O0, já que o compilador poderá 'sumir' com
    o código, por causa da otimização.

    Para i386 e x86-64 defina SYNC_MEM se quiser uma
    serialização mais completa.
   ========================================== */
#include <stdint.h>

uint64_t $local_tsc__;

#if defined(__x86_64__)
  inline void BEGIN_TSC( void )
  {
    uint32_t a, d;

    __asm__ __volatile__ (
# ifdef SYNC_MEM
      "mfence\n\t"
# endif
      "xorl %%eax,%%eax\n\t"
      "cpuid\n\t"
      "rdtsc" : "=a" (a), "=d" (d) :: "%rbx", "%rcx"
    );

    $local_tsc__ = a + (uint64_t)d << 32;
  }

  inline uint64_t END_TSC( void )
  {
    uint32_t a, d;

    __asm__ __volatile__ (
      "rdtscp" : "=a" (a), "=d" (d) :: "%rcx"
    );

    return (a + (uint64_t)d << 32) - $local_tsc__;
  }
#elif defined(__i386__)
  inline void BEGIN_TSC( void )
  {
    uint32_t a, d;

    __asm__ __volatile__ (
# ifdef SYNC_MEM
      "mfence\n\t"
# endif
      "xorl %%eax,%%eax\n\t"
      "cpuid\n\t"
      "rdtsc" : "=a" (a), "=d" (d) :: "%ebx", "%ecx"
    );

    $local_tsc__ = a + (uint64_t)d << 32;
  }

  inline uint64_t END_TSC( void )
  {
    uint32_t a, d;

    __asm__ __volatile__ (
      "rdtscp" : "=a" (a), "=d" (d) :: "%ecx"
    );

    return (a + (uint64_t)d << 32) - $local_tsc__;
  }
#elif defined(__aarch64__)
  inline void BEGIN_TSC( void )
  {
    uint64_t count;

    __asm__ __volatile__ ( "mrs %0,cntvct_el0" : "=r" (count) );

    $local_tsc__ = count;
  }

  inline uint64_t END_TSC( void )
  {
    uint64_t count;

    __asm__ __volatile__ ( "mrs %0,cntvct_el0" : "=r" (count) );

    return count - $local_tsc__;
  }
#else
# error i386, x86-64 and AArch64 only.
#endif

#endif

Se quiser que BEGIN_TSC() espere por todas as leituras e escritas pendentes (para diminuir os efeitos do cache L1), defina o símbolo SYNC_MEM antes de incluir esse header, como em:

#define SYNC_MEM
#include "cycle_counting.h"

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...