#include <cstdint>
#define IM_REP1(X) X "\n\t"
#define IM_REP2(X) IM_REP1(X) IM_REP1(X)
#define IM_REP4(X) IM_REP2(X) IM_REP2(X)
#define IM_REP8(X) IM_REP4(X) IM_REP4(X)
#define IM_REP16(X) IM_REP8(X) IM_REP8(X)
#define IM_REP32(X) IM_REP16(X) IM_REP16(X)
#define IM_REP64(X) IM_REP32(X) IM_REP32(X)
#define IM_REP128(X) IM_REP64(X) IM_REP64(X)
#define IM_REP256(X) IM_REP128(X) IM_REP128(X)
#define IM_REP512(X) IM_REP256(X) IM_REP256(X)
#define IM_REP1K(X) IM_REP512(X) IM_REP512(X)
#define IM_REP2K(X) IM_REP1K(X) IM_REP1K(X)
#define IM_REP4K(X) IM_REP2K(X) IM_REP2K(X)
#define IM_REP8K(X) IM_REP4K(X) IM_REP4K(X)
#define IM_REP16K(X) IM_REP8K(X) IM_REP8K(X)
#define IM_REP32K(X) IM_REP16K(X) IM_REP16K(X)
#define IM_REP64K(X) IM_REP32K(X) IM_REP32K(X)
#define IM_REP128K(X) IM_REP64K(X) IM_REP64K(X)
#define IM_REP256K(X) IM_REP128K(X) IM_REP128K(X)
#define IM_REP512K(X) IM_REP256K(X) IM_REP256K(X)
#define IM_REP1M(X) IM_REP512K(X) IM_REP512K(X)
// By default each instruction listing is repeated 2^12 times before being
// surrounded by timing markers. Then the measured time is divided by 1024
// to compute the average time for each single run of the listing.
// To disable this repetition, define this macro before including the header.
#ifndef INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS
#define INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS (1 << 13)
#endif
// The number of times each measurement cycle is executed and subsequently
// averaged
#ifndef INSTRUCTION_MEASUREMENT_NUM_RUN_REPS
#define INSTRUCTION_MEASUREMENT_NUM_RUN_REPS (1 << 13)
#endif
#if INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP1(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 1
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP2(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 2
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP4(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 3
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP8(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 4
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP16(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 5
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP32(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 6
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP64(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 7
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP128(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 8
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP256(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 9
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP512(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 10
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP1K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 11
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP2K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 12
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP4K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 13
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP8K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 14
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP16K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 15
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP32K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 16
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP64K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 17
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP128K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 18
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP256K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 19
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP512K(X)
#elif INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS == 1 << 20
#define INSTRUCTION_MEASUREMENT_INSERT_LISTING(X) IM_REP1M(X)
#else
#error "Invalid value for INSTRUCTION_MEASUREMENT_REPEAT_LISTING acceptable \
values are (2^p) where p >= 0 && p <= 20"
#endif
#define INSTRUCTION_MEASUREMENT_MARK_START \
"mfence \n\t" \
"lfence \n\t" \
"rdtsc \n\t" \
"lfence \n\t" \
"shl $32, %%rdx \n\t" \
"or %%rax, %%rdx \n\t" \
"movq %%rdx, %[ticks] \n\t"
#define INSTRUCTION_MEASUREMENT_MARK_END \
"mfence \n\t" \
"lfence \n\t" \
"rdtsc \n\t" \
"lfence \n\t" \
"shl $32, %%rdx \n\t" \
"or %%rax, %%rdx \n\t" \
"xchgq %%rdx, %[ticks] \n\t" \
"subq %%rdx, %[ticks] \n\t"
#define INSTRUCTION_MEASUREMENT_DEFINE(NAME, INST_TEMPLATE, INIT, FINAL, \
CLOBBER, MEMSIZE1, MEMSIZE2) \
float InstructionMeasurementFunc##NAME() \
{ \
int64_t totalTicks = 0; \
int64_t ticks = 0; \
int64_t counter = INSTRUCTION_MEASUREMENT_NUM_RUN_REPS; \
alignas(16) char mem1[MEMSIZE1] { 0 }; \
alignas(16) char mem2[MEMSIZE2] { 0 }; \
asm volatile ( \
INIT "\n" \
"%=:\t" \
INSTRUCTION_MEASUREMENT_MARK_START \
INSTRUCTION_MEASUREMENT_INSERT_LISTING(INST_TEMPLATE) \
INSTRUCTION_MEASUREMENT_MARK_END \
"addq %[ticks], %[totalTicks] \n\t" \
INSTRUCTION_MEASUREMENT_MARK_START \
INSTRUCTION_MEASUREMENT_MARK_END \
"subq %[ticks], %[totalTicks] \n\t" \
"decq %[counter] \n\t" \
"jnz %=b\n\t" \
FINAL \
: [totalTicks] "+r" (totalTicks), [ticks] "+r" (ticks), \
[counter] "+r" (counter), \
[mem1] "=m" (mem1), [mem2] "=m" (mem2) \
: [addrMem1] "r" (&mem1), [addrMem2] "r" (&mem2) \
: "rax", "rdx" CLOBBER); \
return static_cast<float>(totalTicks) / \
INSTRUCTION_MEASUREMENT_NUM_RUN_REPS / \
INSTRUCTION_MEASUREMENT_NUM_LISTING_REPS; \
}
#define INSTRUCTION_MEASUREMENT_DO(NAME) \
::instmeas::funcs::internal::InstructionMeasurementFunc##NAME()
#define INSTRUCTION_MEASUREMENT_REG_CLOBBER(...) , __VA_ARGS__
namespace instmeas
{
namespace funcs
{
namespace internal
{
INSTRUCTION_MEASUREMENT_DEFINE(XOR_I32_R64_XOR_R64_IDIV64,
"xorq %%rdx,%%rdx\n\txorq $0x7fffffff,%%rax\n\tidiv %%rbx",
"mov $25393642,%%rax\n\tmov $5039,%%rbx", ,
INSTRUCTION_MEASUREMENT_REG_CLOBBER("rbx"), 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(MOV_I_R64,
"mov $0x8d13fd2583b74e96,%%rax", , , , 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(XOR_R64,
"xorq %%rdx,%%rdx", "movq $0xbbbbbbbbbbbbbbbb,%%rdx", , , 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(XOR_I32_R64,
"xorq $0x7fffffff,%%rax", "movq $0xbbbbbbbbbbbbbbbb,%%rax", , , 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(INC_R64, "inc %%rbx", "xor %%rbx,%%rbx", ,
INSTRUCTION_MEASUREMENT_REG_CLOBBER("rbx"), 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(DEC_R64,
"dec %%rbx", "movq $0xffffffff,%%rbx", ,
INSTRUCTION_MEASUREMENT_REG_CLOBBER("rbx"), 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(INC_M64,
"incq %[mem1]", "movq $0,%[mem1]", , , 8, 1)
INSTRUCTION_MEASUREMENT_DEFINE(DEC_M64,
"decq %[mem1]", "movq $0x7fffffff,%[mem1]", , , 8, 1)
INSTRUCTION_MEASUREMENT_DEFINE(INC_M32,
"incl %[mem1]", "movl $0,%[mem1]", , , 4, 1)
INSTRUCTION_MEASUREMENT_DEFINE(DEC_M32,
"decl %[mem1]", "movl $0x7fffffff,%[mem1]", , , 4, 1)
INSTRUCTION_MEASUREMENT_DEFINE(FMUL_ST_ST, "fmul %%st(0),%%st(1)",
"movl $0x3f8020c5,%[mem1]\n\tfldl %[mem1]\n\tfldpi", ,
INSTRUCTION_MEASUREMENT_REG_CLOBBER("st"), 4, 1)
INSTRUCTION_MEASUREMENT_DEFINE(FLDPI, "fldpi", , , , 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(FLDPI_FSQRT_FSTP,
"fldpi\n\tfsqrt\n\tfstp %%st(0)", , ,
INSTRUCTION_MEASUREMENT_REG_CLOBBER("st"), 1, 1)
INSTRUCTION_MEASUREMENT_DEFINE(FLDPI_FSTP, "fldpi\n\tfstp %%st(0)", , ,
INSTRUCTION_MEASUREMENT_REG_CLOBBER("st"), 1, 1)
} // namespace internal
float IDIV_R64()
{
return INSTRUCTION_MEASUREMENT_DO(XOR_I32_R64_XOR_R64_IDIV64) -
INSTRUCTION_MEASUREMENT_DO(XOR_R64) -
INSTRUCTION_MEASUREMENT_DO(XOR_I32_R64);
}
float XOR_R64()
{
return INSTRUCTION_MEASUREMENT_DO(XOR_R64);
}
float XOR_I32_R64()
{
return INSTRUCTION_MEASUREMENT_DO(XOR_I32_R64);
}
float MOV_R_I64()
{
return INSTRUCTION_MEASUREMENT_DO(MOV_I_R64);
}
float INC_R64()
{
return INSTRUCTION_MEASUREMENT_DO(INC_R64);
}
float DEC_R64()
{
return INSTRUCTION_MEASUREMENT_DO(DEC_R64);
}
float INC_M64()
{
return INSTRUCTION_MEASUREMENT_DO(INC_M64);
}
float DEC_M64()
{
return INSTRUCTION_MEASUREMENT_DO(DEC_M64);
}
float INC_M32()
{
return INSTRUCTION_MEASUREMENT_DO(INC_M32);
}
float DEC_M32()
{
return INSTRUCTION_MEASUREMENT_DO(DEC_M32);
}
float FMUL_ST_ST()
{
return INSTRUCTION_MEASUREMENT_DO(FMUL_ST_ST);
}
float FSQRT()
{
return INSTRUCTION_MEASUREMENT_DO(FLDPI_FSQRT_FSTP) -
INSTRUCTION_MEASUREMENT_DO(FLDPI_FSTP);
}
} // namespace funcs
} // namespace instmeas
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#define COUT_MEASURE(X) std::cout << std::setw(12) << std::left << #X ":" \
<< std::setprecision(2) << std::fixed << std::setw(6) << std::right << X() \
<< "\n"
void DumpCPUInfo()
{
std::fstream file("/proc/cpuinfo", std::fstream::in);
std::ostringstream stream;
while (file.good())
{
char buffer[256];
file.read(buffer, sizeof(buffer));
stream.write(buffer, sizeof(buffer));
}
auto str = stream.str();
std::regex modelNameRegex(R"(model name\s*\:\s*(.*)\r?\n?)");
auto matchBegin = std::sregex_iterator(
str.cbegin(), str.cend(), modelNameRegex);
auto cpuNum = 0;
for (auto it = matchBegin; it != std::sregex_iterator(); ++it, ++cpuNum)
{
auto match = *it;
if (match.size() > 0)
{
std::cout << "Processor #" << cpuNum << " model name: "
<< match[1].str() << "\n";
}
}
}
int main()
{
using namespace instmeas::funcs;
DumpCPUInfo();
std::cout << "Beginning measurement...\n\n";
COUT_MEASURE(IDIV_R64);
COUT_MEASURE(XOR_R64);
COUT_MEASURE(XOR_I32_R64);
COUT_MEASURE(MOV_R_I64);
COUT_MEASURE(INC_R64);
COUT_MEASURE(DEC_R64);
COUT_MEASURE(INC_M64);
COUT_MEASURE(DEC_M64);
COUT_MEASURE(INC_M32);
COUT_MEASURE(DEC_M32);
COUT_MEASURE(FMUL_ST_ST);
// COUT_MEASURE(FSQRT); // Beat the processor serialization then uncomment
std::cout << "\nFinished measurement!" << std::endl;
return 0;
}