C is often called "portable assembly," but sometimes you need the real thing.

If you need to read the CPU Time Stamp Counter (RDTSC) for nanosecond-precision benchmarking, access specific processor flags, or utilize SIMD instructions not exposed by your C compiler, you can write raw assembly and call it directly from Perl.

The Recipe

This time, we'll implement a function to read the CPU cycle count (RDTSC) on x86-64 Linux/Unix/Windows systems.

use v5.40;
use Affix qw[:all];
use Affix::Build;

# 1. Write Assembly
# We use AT&T syntax (standard for GCC/Clang on Unix).
# This file (.s) will be passed to the assembler.
my $c = Affix::Build->new( name => 'asm_lib' );
$c->add( \<<~'END', lang => 's' );
    .text
    .global get_ticks

    get_ticks:
        # rdtsc puts 32 bits in EDX (high) and 32 bits in EAX (low)
        rdtsc

        # We need to pack them into a single 64-bit register (RAX)
        # Shift RDX left by 32 bits
        shl $32, %rdx

        # Combine RDX and RAX
        or %rdx, %rax

        # Return value is in RAX
        ret
END

# 2. Link
# Note: On macOS, symbols need a leading underscore ('_get_ticks').
my $lib = $c->link;

# 3. Bind
# The function takes no args and returns a 64-bit unsigned integer.
affix $lib, 'get_ticks', [] => UInt64;

# 4. Benchmark
say 'Measuring CPU cycles for a Perl operation...';
my $start = get_ticks();

# Do some work
my $x = 0;
$x += $_ for 1 .. 1000;

# Check the clock again
my $end = get_ticks();
say 'Start: ' . $start;
say 'End:   ' . $end;
say 'Diff:  ' . ( $end - $start ) . ' cycles';

On my system, the output looks like this:

Measuring CPU cycles for a Perl operation...
Start: 4098233110110909
End:   4098233110188467
Diff:  77558 cycles

How It Works

Kitchen Reminders