invoking-relative-references.c
#include <mach/mach_time.h> | |
#include <mach-o/dyld.h> | |
#include <inttypes.h> | |
#include <signal.h> | |
#include <spawn.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/param.h> | |
#include <sys/wait.h> | |
static const unsigned NUM_CLASSES = 8*8*8*8; | |
static const unsigned NUM_RUNS = 20*1000; | |
#if !defined(VARIATION) || VARIATION < 0 || VARIATION > 1 | |
#error "Please pass -DVARIATION=[01] to the compiler. " \ | |
"0 = relative method references; 1 = absolute method pointers" | |
#endif | |
#if VARIATION == 0 | |
typedef int method_t; | |
#else | |
typedef void (*method_t)(void); | |
#endif | |
struct class { | |
method_t method1; | |
method_t method2; | |
method_t method3; | |
method_t method4; | |
method_t method5; | |
method_t method6; | |
method_t method7; | |
method_t method8; | |
}; | |
void method_implementation(void) {} | |
// Emit something 4096 times | |
#define LOTS(x) \ | |
x(__COUNTER__) x(__COUNTER__) x(__COUNTER__) x(__COUNTER__) \ | |
x(__COUNTER__) x(__COUNTER__) x(__COUNTER__) x(__COUNTER__) | |
#define LOTS_AND_LOTS(x) \ | |
LOTS(x) LOTS(x) LOTS(x) LOTS(x) \ | |
LOTS(x) LOTS(x) LOTS(x) LOTS(x) | |
#define LOTS_AND_LOTS_AND_LOTS(x) \ | |
LOTS_AND_LOTS(x) LOTS_AND_LOTS(x) LOTS_AND_LOTS(x) LOTS_AND_LOTS(x) \ | |
LOTS_AND_LOTS(x) LOTS_AND_LOTS(x) LOTS_AND_LOTS(x) LOTS_AND_LOTS(x) | |
#define LOTS_AND_LOTS_AND_LOTS_AND_LOTS(x) \ | |
LOTS_AND_LOTS_AND_LOTS(x) LOTS_AND_LOTS_AND_LOTS(x) LOTS_AND_LOTS_AND_LOTS(x) LOTS_AND_LOTS_AND_LOTS(x) \ | |
LOTS_AND_LOTS_AND_LOTS(x) LOTS_AND_LOTS_AND_LOTS(x) LOTS_AND_LOTS_AND_LOTS(x) LOTS_AND_LOTS_AND_LOTS(x) | |
#define CAT_(a, b) a ## b | |
#define CAT(a, b) CAT_(a, b) | |
#if VARIATION == 0 | |
// Variation 0 -- relative references | |
extern volatile struct class classes[NUM_CLASSES]; | |
// Create 4096 class records. | |
// We have to generate the classes using global inline assembler, since C does not | |
// support constant expressions involving arithmetic on integers cast from global | |
// pointers. | |
__asm__ ( | |
".section __TEXT, __const\n" | |
"_classes:\n" | |
#define CLASS_(nam) \ | |
" .long _method_implementation - .\n" \ | |
" .long _method_implementation - .\n" \ | |
" .long _method_implementation - .\n" \ | |
" .long _method_implementation - .\n" \ | |
" .long _method_implementation - .\n" \ | |
" .long _method_implementation - .\n" \ | |
" .long _method_implementation - .\n" \ | |
" .long _method_implementation - .\n" | |
#define CLASS(nam) CLASS_(nam) | |
#define OF_CLASSES(n) CLASS(CAT(class, n)) | |
LOTS_AND_LOTS_AND_LOTS_AND_LOTS(OF_CLASSES) | |
); | |
static inline void *resolve_offset(volatile int *offset_ptr) { | |
uintptr_t base = (uintptr_t)offset_ptr; | |
uintptr_t offset = (uintptr_t)(intptr_t)*offset_ptr; | |
return (void*)(base + offset); | |
} | |
static inline void invoke_method(volatile int *method_offset) { | |
void (*method_ptr)(void) = (void (*)(void))resolve_offset(method_offset); | |
method_ptr(); | |
} | |
#else | |
// Variation 1 -- absolute pointers | |
volatile struct class classes[NUM_CLASSES] = { | |
#define CLASS_(nam) \ | |
{ \ | |
.method1 = method_implementation, \ | |
.method2 = method_implementation, \ | |
.method3 = method_implementation, \ | |
.method4 = method_implementation, \ | |
.method5 = method_implementation, \ | |
.method6 = method_implementation, \ | |
.method7 = method_implementation, \ | |
.method8 = method_implementation, \ | |
}, | |
#define CLASS(nam) CLASS_(nam) | |
#define OF_CLASSES(n) CLASS(CAT(class, n)) | |
LOTS_AND_LOTS_AND_LOTS_AND_LOTS(OF_CLASSES) | |
}; | |
static inline void invoke_method(volatile method_t *method_ptr) { | |
(*method_ptr)(); | |
} | |
#endif | |
int main(int argc, char *argv[], char *envp[]) { | |
uint64_t start_time = mach_absolute_time(); | |
// Invoke all the methods on all the classes. | |
for (unsigned i = 0; i < NUM_RUNS; ++i) { | |
for (volatile struct class *c = classes, *end = classes + NUM_CLASSES; | |
c != end; ++c) { | |
invoke_method(&c->method1); | |
invoke_method(&c->method2); | |
invoke_method(&c->method3); | |
invoke_method(&c->method4); | |
invoke_method(&c->method5); | |
invoke_method(&c->method6); | |
invoke_method(&c->method7); | |
invoke_method(&c->method8); | |
} | |
} | |
uint64_t end_time = mach_absolute_time(); | |
uint64_t elapsed = end_time - start_time; | |
// Convert mach time units to nanoseconds. | |
mach_timebase_info_data_t timebase; | |
mach_timebase_info(&timebase); | |
typedef unsigned __int128 uint128_t; | |
uint64_t nanoseconds = (uint128_t)elapsed * (uint128_t)timebase.numer / timebase.denom; | |
printf("%" PRIu64 " nanoseconds to invoke methods\n", nanoseconds); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment