Skip to content

Instantly share code, notes, and snippets.

@kklobe
Last active October 12, 2023 20:33
Show Gist options
  • Save kklobe/e9ff3cb0d455c3ff659c5d3d5678be54 to your computer and use it in GitHub Desktop.
Save kklobe/e9ff3cb0d455c3ff659c5d3d5678be54 to your computer and use it in GitHub Desktop.
X86PageEntry microbenchmark
#include <cstdint>
#include <chrono>
#include <iostream>
#include <vector>
#ifdef _MSC_VER
#pragma pack (1)
#endif
struct X86_PageEntryBlock{
#ifdef WORDS_BIGENDIAN
uint32_t base:20;
uint32_t avl:3;
uint32_t g:1;
uint32_t pat:1;
uint32_t d:1;
uint32_t a:1;
uint32_t pcd:1;
uint32_t pwt:1;
uint32_t us:1;
uint32_t wr:1;
uint32_t p:1;
#else
uint32_t p:1;
uint32_t wr:1;
uint32_t us:1;
uint32_t pwt:1;
uint32_t pcd:1;
uint32_t a:1;
uint32_t d:1;
uint32_t pat:1;
uint32_t g:1;
uint32_t avl:3;
uint32_t base:20;
#endif
} __attribute__((packed));
#ifdef _MSC_VER
#pragma pack ()
#endif
union X86PageEntry {
uint32_t load = 0;
X86_PageEntryBlock block;
};
struct X86PageEntryNew {
#ifdef WORDS_BIGENDIAN
uint32_t base:20;
uint32_t avl:3;
uint32_t g:1;
uint32_t pat:1;
uint32_t d:1;
uint32_t a:1;
uint32_t pcd:1;
uint32_t pwt:1;
uint32_t us:1;
uint32_t wr:1;
uint32_t p:1;
#else
uint32_t p:1;
uint32_t wr:1;
uint32_t us:1;
uint32_t pwt:1;
uint32_t pcd:1;
uint32_t a:1;
uint32_t d:1;
uint32_t pat:1;
uint32_t g:1;
uint32_t avl:3;
uint32_t base:20;
#endif
constexpr void set(const uint32_t value) {
#ifdef WORDS_BIGENDIAN
base = (value >> 12) & 0xFFFFF;
avl = (value >> 9) & 0x7;
g = (value >> 8) & 0x1;
pat = (value >> 7) & 0x1;
d = (value >> 6) & 0x1;
a = (value >> 5) & 0x1;
pcd = (value >> 4) & 0x1;
pwt = (value >> 3) & 0x1;
us = (value >> 2) & 0x1;
wr = (value >> 1) & 0x1;
p = value & 0x1;
#else
p = value & 0x1;
wr = (value >> 1) & 0x1;
us = (value >> 2) & 0x1;
pwt = (value >> 3) & 0x1;
pcd = (value >> 4) & 0x1;
a = (value >> 5) & 0x1;
d = (value >> 6) & 0x1;
pat = (value >> 7) & 0x1;
g = (value >> 8) & 0x1;
avl = (value >> 9) & 0x7;
base = (value >> 12) & 0xFFFFF;
#endif
}
constexpr uint32_t get() const {
uint32_t value = 0;
#ifdef WORDS_BIGENDIAN
value |= (base << 12);
value |= (avl << 9);
value |= (g << 8);
value |= (pat << 7);
value |= (d << 6);
value |= (a << 5);
value |= (pcd << 4);
value |= (pwt << 3);
value |= (us << 2);
value |= (wr << 1);
value |= p;
#else
value |= p;
value |= (wr << 1);
value |= (us << 2);
value |= (pwt << 3);
value |= (pcd << 4);
value |= (a << 5);
value |= (d << 6);
value |= (pat << 7);
value |= (g << 8);
value |= (avl << 9);
value |= (base << 12);
#endif
return value;
}
};
int main() {
const std::size_t numIterations = 100000000;
std::vector<uint32_t> values(numIterations);
for (std::size_t i = 0; i < numIterations; ++i) {
values[i] = static_cast<uint32_t>(i);
}
auto start = std::chrono::high_resolution_clock::now();
// Test old union/struct
X86PageEntry oldEntry;
for (std::size_t i = 0; i < numIterations; ++i) {
oldEntry.load = values[i];
volatile uint32_t tmp = oldEntry.load; // Prevent optimization
}
auto end = std::chrono::high_resolution_clock::now();
auto durationOld = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
std::cout << "Old struct/union duration: " << durationOld << " microseconds\n";
start = std::chrono::high_resolution_clock::now();
// Test new struct
X86PageEntryNew newEntry;
for (std::size_t i = 0; i < numIterations; ++i) {
newEntry.set(values[i]);
volatile uint32_t tmp = newEntry.get(); // Prevent optimization
}
end = std::chrono::high_resolution_clock::now();
auto durationNew = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
std::cout << "New struct duration: " << durationNew << " microseconds\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment