Last active
April 18, 2017 07:09
-
-
Save xaxxon/465dc092eb9b8bac4f01685d25b3200b to your computer and use it in GitHub Desktop.
fast string class and benchmark
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
#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); |
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 "../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(); |
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
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