Last active
August 6, 2021 14:28
-
-
Save kammce/fcbf226c31afb694f68e6c3a9b5cd78d to your computer and use it in GitHub Desktop.
[Benchmark] C-style multibit insert vs C++ bitset
This file contains hidden or 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 <bitset> | |
#include <cinttypes> | |
#include <cstdio> | |
#include <limits> | |
#include <type_traits> | |
namespace xstd { | |
struct bitrange { | |
uint32_t position; | |
uint32_t width; | |
template <typename T> | |
constexpr auto mask() const | |
{ | |
// Need to use an unsigned version of the type T for the mask to make sure | |
// that the shift right doesn't result in a sign extended shift. | |
using UnsignedT = typename std::make_unsigned<T>::type; | |
// At compile time, generate variable containing all 1s with the size of the | |
// target parameter. | |
constexpr UnsignedT kFieldOfOnes = std::numeric_limits<UnsignedT>::max(); | |
// At compile time calculate the number of bits in the target parameter. | |
constexpr size_t kTargetWidth = sizeof(T) * 8; | |
// Create mask by shifting the set of 1s down so that the number of 1s from | |
// bit position 0 is equal to the width parameter. | |
UnsignedT bitmask_at_origin = static_cast<UnsignedT>(kFieldOfOnes >> (kTargetWidth - width)); | |
UnsignedT bitmask = static_cast<UnsignedT>(bitmask_at_origin << position); | |
return bitmask; | |
} | |
template <typename T> | |
constexpr auto inverted_mask() const | |
{ | |
return ~mask<T>(); | |
} | |
}; | |
template <typename T> | |
class bitset : public std::bitset<sizeof(T) * 8> { | |
public: | |
bitset(T& register_reference) | |
: std::bitset<sizeof(T) * 8>(register_reference) | |
, register_reference_(register_reference) | |
{ | |
} | |
void save() { register_reference_ = static_cast<T>(this->to_ulong()); } | |
template <bitrange field, typename U> | |
constexpr auto& insert(U value) | |
{ | |
auto kBitmask = field.mask<std::remove_volatile_t<T>>(); | |
auto kInvertedBitmask = field.inverted_mask<std::remove_volatile_t<T>>(); | |
// Clear width's number of bits in the target value at the bit position | |
// specified. | |
*this &= kInvertedBitmask; | |
// AND value with mask to remove any bits beyond the specified width. | |
// Shift masked value into bit position and OR with target value. | |
*this |= (value << field.position) & kBitmask; | |
return *this; | |
} | |
template <bitrange mask> | |
[[nodiscard]] constexpr auto extract() | |
{ | |
// Create mask by shifting the set of 1s down so that the number of 1s | |
// from bit position 0 is equal to the width parameter. | |
return std::bitset<mask.width>(this->to_ullong() >> mask.position); | |
} | |
~bitset() { save(); } | |
T& register_reference_; | |
}; | |
} | |
struct Register { | |
volatile uint32_t CTRL1; | |
volatile uint32_t CTRL2; | |
}; | |
Register original; | |
Register * reg = &original; | |
constexpr uint16_t kTestValue = 0x01AA; | |
static void ClearThenSet(benchmark::State& state) { | |
for (auto _ : state) { | |
reg->CTRL1 &= ~(0xFF << 8); | |
reg->CTRL1 |= (kTestValue << 8) & 0xFF; | |
} | |
} | |
BENCHMARK(ClearThenSet); | |
static void SingleLineAssignment(benchmark::State& state) { | |
for (auto _ : state) { | |
reg->CTRL1 = (reg->CTRL1 & ~(0xFF << 8)) | ((kTestValue & 0xFF) << 8); | |
} | |
} | |
BENCHMARK(SingleLineAssignment); | |
static void FastBitmask(benchmark::State& state) { | |
for (auto _ : state) { | |
static constexpr uint32_t kMask = 0xFF; | |
static constexpr uint32_t kMaskInverted = ~(kMask << 8); | |
auto temp = reg->CTRL1; | |
temp = (temp & kMaskInverted) | ((kTestValue & kMask) << 8); | |
reg->CTRL1 = temp; | |
} | |
} | |
BENCHMARK(FastBitmask); | |
static void UsingBitset(benchmark::State& state) { | |
for (auto _ : state) { | |
static constexpr xstd::bitrange kIntensity { 16, 8 }; | |
xstd::bitset(reg->CTRL1).insert<kIntensity>(kTestValue); | |
} | |
} | |
BENCHMARK(UsingBitset); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment