Skip to content

Instantly share code, notes, and snippets.

@xaxxon
Last active April 18, 2017 07:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xaxxon/465dc092eb9b8bac4f01685d25b3200b to your computer and use it in GitHub Desktop.
Save xaxxon/465dc092eb9b8bac4f01685d25b3200b to your computer and use it in GitHub Desktop.
fast string class and benchmark
#pragma once
#include <type_traits>
#include <memory>
#include <string>
#include <string_view>
#include <new>
#include <cstdint>
#include <assert.h>
#include <emmintrin.h>
#include <iostream>
template<size_t alignment>
std::unique_ptr<char[]> allocate_aligned_blocks(size_t minimum_bytes) {
// get the size >= minimum size that is a multiple of the alignment 0 => 0, 1 => alignment, alignment => alignmnet, alignment + 1 => 2*alignment
size_t actual_bytes = (minimum_bytes + (alignment - 1)) & ~(alignment-1);
auto buffer = new(static_cast<std::align_val_t>(alignment)) char[actual_bytes]();
memset(buffer, 0, actual_bytes);
assert(buffer[actual_bytes-1] == '\0');
assert(((intptr_t)buffer & alignment-1) == 0);
return std::unique_ptr<char[]>(buffer);
}
template<size_t alignment>
class AlignedString {
static_assert(alignment % 16 == 0, "Aligned string requires 128-bit alignment");
private:
std::unique_ptr<char[]> buffer;
uint32_t size;
public:
AlignedString(std::string_view const & string_view) :
buffer(allocate_aligned_blocks<alignment>(string_view.length() + 1)),
size(string_view.length())
{
memcpy((void *)buffer.get(), string_view.data(), string_view.length());
}
AlignedString(char const * source) :
AlignedString(std::string_view(source, strlen(source)))
{}
AlignedString(std::string const & source) :
AlignedString(std::string_view(source.data(), source.length()))
{}
AlignedString(AlignedString && other) = default;
AlignedString(AlignedString const & other);
AlignedString & operator=(AlignedString && other);
template<size_t eq_alignment = alignment, std::enable_if_t<eq_alignment % 64 != 0, int> = 0>
bool operator==(AlignedString const & other) const {
if (this->size != other.size) {
return false;
}
size_t offset = 0;
while(offset < this->size) {
auto str1 = _mm_load_si128((__m128i *) this->buffer.get());
auto str2 = _mm_load_si128((__m128i *) other.buffer.get());
auto result = _mm_cmpeq_epi8(str1, str2);
uint16_t mask = ~_mm_movemask_epi8(result);
if (mask) {
return false;
}
offset += 16;
}
// std::cerr << mask << std::endl;
return true;
}
template<size_t eq_alignment = alignment, std::enable_if_t<eq_alignment % 64 == 0, int> = 0>
bool operator==(AlignedString const & other) const {
if (this->size != other.size) {
return false;
}
size_t offset = 0;
while(offset < this->size) {
auto str1_0 = _mm_load_si128((__m128i *) (this->buffer.get() + offset));
auto str1_1 = _mm_load_si128((__m128i *) (this->buffer.get() + 16 + offset));
auto str1_2 = _mm_load_si128((__m128i *) (this->buffer.get() + 32 + offset));
auto str1_3 = _mm_load_si128((__m128i *) (this->buffer.get() + 48 + offset));
auto str2_0 = _mm_load_si128((__m128i *) (other.buffer.get() + offset));
auto str2_1 = _mm_load_si128((__m128i *) (other.buffer.get() + 16 + offset));
auto str2_2 = _mm_load_si128((__m128i *) (other.buffer.get() + 32 + offset));
auto str2_3 = _mm_load_si128((__m128i *) (other.buffer.get() + 48 + offset));
auto result0 = _mm_cmpeq_epi8(str1_0, str2_0);
auto result1 = _mm_cmpeq_epi8(str1_1, str2_1);
auto result2 = _mm_cmpeq_epi8(str1_2, str2_2);
auto result3 = _mm_cmpeq_epi8(str1_3, str2_3);
auto mask = _mm_movemask_epi8(_mm_and_si128(_mm_and_si128(result0, result1), _mm_and_si128(result2, result3)));
if (mask != 0xffff) {
return false;
}
offset += 64;
}
return true;
}
operator std::string_view() const {
return {(char *)this->buffer.get(), this->size};
}
bool operator<(AlignedString const & other) const;
size_t strchr(char c) const;
char const * c_str() const {
return this->buffer.get();
}
};
template<size_t alignment>
std::ostream & operator<<(std::ostream const & os, AlignedString<alignment> const & aligned_string);
#include "../include/aligned_string.h"
#include <benchmark/benchmark.h>
#include <memory>
using namespace std;
static void compare_16char_string16_for_equality(benchmark::State& state) {
AlignedString<16> string1("0123456789abcdef");
AlignedString<16> string2("0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_16char_string16_for_equality);
static void compare_64char_string16_for_equality(benchmark::State& state) {
AlignedString<16> string1("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
AlignedString<16> string2("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_64char_string16_for_equality);
static void compare_128char_string16_for_equality(benchmark::State& state) {
AlignedString<16> string1("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
AlignedString<16> string2("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_128char_string16_for_equality);
static void compare_string16_for_inequality(benchmark::State& state) {
AlignedString<16> string1("0");
AlignedString<16> string2("1");
assert(!(string1 == string2));
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_string16_for_inequality);
static void compare_16char_string64_for_equality(benchmark::State& state) {
AlignedString<64> string1("0123456789abcdef");
AlignedString<64> string2("0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_16char_string64_for_equality);
static void compare_64char_string64_for_equality(benchmark::State& state) {
AlignedString<64> string1("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
AlignedString<64> string2("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_64char_string64_for_equality);
static void compare_128char_string64_for_equality(benchmark::State& state) {
AlignedString<64> string1("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
AlignedString<64> string2("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_128char_string64_for_equality);
static void compare_string64_for_inequality(benchmark::State& state) {
AlignedString<64> string1("0");
AlignedString<64> string2("1");
assert(!(string1 == string2));
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_string64_for_inequality);
static void compare_16char_std_strings_for_equality(benchmark::State& state) {
std::string string1("0123456789abcdef");
std::string string2("0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_16char_std_strings_for_equality);
static void compare_64char_std_strings_for_equality(benchmark::State& state) {
std::string string1("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
std::string string2("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_64char_std_strings_for_equality);
static void compare_128char_std_strings_for_equality(benchmark::State& state) {
std::string string1("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
std::string string2("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
assert(string1 == string2);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_128char_std_strings_for_equality);
static void compare_std_string_for_inequality(benchmark::State& state) {
std::string string1("0");
std::string string2("1");
assert(!(string1 == string2));
while (state.KeepRunning()) {
benchmark::DoNotOptimize(string1 == string2);
}
}
BENCHMARK(compare_string64_for_inequality);
BENCHMARK_MAIN();
clang++ aligned_string.cpp -std=c++1z -L $CLANG_HOME/lib -I.. -lbenchmark -O3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment