Created
April 28, 2022 17:55
-
-
Save umgefahren/fb071e706d1d73c5eb2b0c0f400a727c to your computer and use it in GitHub Desktop.
Implementation of fast enum in C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <limits.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <time.h> | |
#define ONE 1 | |
// #define PRINT_OUPUT | |
const uint8_t uint8_one = ONE; | |
const uint16_t uint16_one = ONE; | |
const uint32_t uint32_one = ONE; | |
const uint64_t uint64_one = ONE; | |
union NumVariantUnion { | |
uint8_t u8; | |
uint16_t u16; | |
uint32_t u32; | |
uint64_t u64; | |
}; | |
enum NumVariantEnum { u8, u16, u32, u64 }; | |
struct NumVariant { | |
union NumVariantUnion data; | |
enum NumVariantEnum type; | |
}; | |
enum NumInVariantEnum { u8In, u16In, u32In, u64In }; | |
void calcVariant(struct NumVariant input); | |
void constructVariant(enum NumInVariantEnum input) { | |
struct NumVariant ret; | |
switch (input) { | |
case u8In: | |
ret.type = u8; | |
ret.data.u8 = UINT8_MAX; | |
break; | |
case u16In: | |
ret.type = u16; | |
ret.data.u16 = UINT16_MAX; | |
break; | |
case u32In: | |
ret.type = u32; | |
ret.data.u32 = UINT32_MAX; | |
break; | |
case u64In: | |
ret.type = u64; | |
ret.data.u64 = UINT64_MAX; | |
break; | |
} | |
calcVariant(ret); | |
} | |
inline void calcVariant(struct NumVariant input) { | |
switch (input.type) { | |
case u8: { | |
uint8_t structData = input.data.u8; | |
structData -= uint8_one; | |
#ifdef PRINT_OUPUT | |
printf("%i\n", structData); | |
#endif | |
break; | |
} | |
case u16: { | |
uint16_t structData = input.data.u16; | |
structData -= uint16_one; | |
#ifdef PRINT_OUPUT | |
printf("%i\n", structData); | |
#endif | |
break; | |
} | |
case u32: { | |
uint32_t structData = input.data.u32; | |
structData -= uint32_one; | |
#ifdef PRINT_OUPUT | |
printf("%u\n", structData); | |
#endif | |
break; | |
} | |
case u64: { | |
uint64_t structData = input.data.u64; | |
structData -= uint64_one; | |
#ifdef PRINT_OUPUT | |
printf("%llu\n", structData); | |
#endif | |
break; | |
} | |
} | |
} | |
void constructVariantASM(enum NumInVariantEnum input) { | |
uint8_t uint8_variant_val; | |
uint16_t uint16_variant_val; | |
uint32_t uint32_variant_val; | |
uint64_t uint64_variant_val; | |
struct NumVariant ret; | |
switch (input) { | |
case u8In: { | |
uint8_variant_val = UINT8_MAX; | |
goto uint8_variant; | |
ret.type = u8; | |
ret.data.u8 = UINT8_MAX; | |
break; | |
} | |
case u16In: { | |
uint16_variant_val = UINT16_MAX; | |
goto uint16_variant; | |
ret.type = u16; | |
ret.data.u16 = UINT16_MAX; | |
break; | |
} | |
case u32In: { | |
uint32_variant_val = UINT32_MAX; | |
goto uint32_variant; | |
ret.type = u32; | |
ret.data.u32 = UINT32_MAX; | |
break; | |
} | |
case u64In: { | |
uint64_variant_val = UINT64_MAX; | |
goto uint64_variant; | |
ret.type = u64; | |
ret.data.u64 = UINT64_MAX; | |
} | |
} | |
switch (ret.type) { | |
case u8: { | |
uint8_t structData = ret.data.u8; | |
uint8_variant: | |
structData = uint8_variant_val; | |
structData -= uint8_one; | |
#ifdef PRINT_OUPUT | |
printf("%i\n", structData); | |
#endif | |
break; | |
} | |
case u16: { | |
uint16_t structData = ret.data.u16; | |
uint16_variant: | |
structData = uint16_variant_val; | |
structData -= uint16_one; | |
#ifdef PRINT_OUPUT | |
printf("%i\n", structData); | |
#endif | |
break; | |
} | |
case u32: { | |
uint32_t structData = ret.data.u32; | |
uint32_variant: | |
structData = uint32_variant_val; | |
structData -= uint32_one; | |
#ifdef PRINT_OUPUT | |
printf("%u\n", structData); | |
#endif | |
break; | |
} | |
case u64: { | |
uint64_t structData = ret.data.u64; | |
uint64_variant: | |
structData = uint64_variant_val; | |
structData -= uint64_one; | |
#ifdef PRINT_OUPUT | |
printf("%llu\n", structData); | |
#endif | |
break; | |
} | |
} | |
} | |
#ifdef PRINT_OUPUT | |
const uint64_t ITERATIONS = 1; | |
#else | |
const uint64_t ITERATIONS = 1E9; | |
#endif | |
void DoNotOptimize(enum NumInVariantEnum input) { | |
asm volatile ("" : : "r,m"(input) : "memory"); | |
} | |
void benchFunction() { | |
#pragma nounroll | |
for (uint64_t i = 0; i < ITERATIONS; i++) { | |
volatile enum NumInVariantEnum inVariant = u8In; | |
DoNotOptimize(inVariant); | |
constructVariant(inVariant); | |
inVariant = u16In; | |
DoNotOptimize(inVariant); | |
constructVariant(inVariant); | |
inVariant = u32In; | |
DoNotOptimize(inVariant); | |
constructVariant(inVariant); | |
inVariant = u64In; | |
DoNotOptimize(inVariant); | |
constructVariant(inVariant); | |
} | |
} | |
void benchASM() { | |
#pragma nounroll | |
for (uint64_t i = 0; i < ITERATIONS; i++) { | |
volatile enum NumInVariantEnum inVariant = u8In; | |
DoNotOptimize(inVariant); | |
constructVariantASM(inVariant); | |
inVariant = u16In; | |
DoNotOptimize(inVariant); | |
constructVariantASM(inVariant); | |
inVariant = u32In; | |
DoNotOptimize(inVariant); | |
constructVariantASM(inVariant); | |
inVariant = u64In; | |
DoNotOptimize(inVariant); | |
constructVariantASM(inVariant); | |
} | |
} | |
int main() { | |
clock_t start, end, time; | |
start = clock(); | |
benchFunction(); | |
end = clock(); | |
time = end - start; | |
double time_taken = ((double)time) / CLOCKS_PER_SEC; | |
printf("Pure took %f seconds\n", time_taken); | |
start = clock(); | |
benchASM(); | |
end = clock(); | |
time = end - start; | |
time_taken = ((double)time) / CLOCKS_PER_SEC; | |
printf("ASM took %f seconds\n", time_taken); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment