Skip to content

Commit

Permalink
Merge pull request #91 from contour-terminal/improvement/add_benchmar…
Browse files Browse the repository at this point in the history
…k_and_std_simd

Add benchmark and std::simd
  • Loading branch information
christianparpart authored Feb 24, 2024
2 parents 9246051 + ad31aa4 commit a051446
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ jobs:
run: cmake --build build/ -- -j3
- name: "test"
run: ./build/src/libunicode/unicode_test
- name: "benchmark"
run: ./build/src/libunicode/libunicode_benchmark


unknown_os:
Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ${MASTER_PROJECT})
option(LIBUNICODE_COVERAGE "libunicode: Builds with codecov [default: OFF]" OFF)
option(LIBUNICODE_EXAMPLES "libunicode: Enables building of example programs. [default: ${MASTER_PROJECT}]" ${MASTER_PROJECT})
option(LIBUNICODE_TESTING "libunicode: Enables building of unittests for libunicode [default: ${MASTER_PROJECT}" ${MASTER_PROJECT})
option(LIBUNICODE_BENCHMARK "libunicode: Enables building of benchmark for libunicode [default: OFF]" OFF)
option(LIBUNICODE_TOOLS "libunicode: Builds CLI tools [default: ${MASTER_PROJECT}]" ${MASTER_PROJECT})
option(LIBUNICODE_BUILD_STATIC "libunicode: provide static library instead of dynamic [default: ${LIBUNICODE_BUILD_STATIC_DEFAULT}]" ${LIBUNICODE_BUILD_STATIC_DEFAULT})
option(LIBUNICODE_USE_INTRINSICS "libunicode: Use SIMD extenstion during text read [default: ON]" ON)
option(LIBUNICODE_USE_STD_SIMD "libunicode: Use std::simd as SIMD extenstion during text read (takes precedence over own intrinsics) [default: ON]" ${LIBUNICODE_USE_INTRINSICS})

set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Enable testing of the benchmark library." FORCE)
include(ThirdParties)

if(LIBUNICODE_TESTING)
Expand Down
74 changes: 74 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 27,
"patch": 0
},
"configurePresets": [
{
"name": "local-devel",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-march=native"
}
},
{
"name": "libunicode-common",
"hidden": true,
"binaryDir": "${sourceDir}/build/${presetName}",
"generator": "Ninja",
"cacheVariables": {
"LIBUNICODE_TESTING": "ON",
"LIBUNICODE_BENCHMARK": "ON",
"PEDANTIC_COMPILER": "OFF",
"PEDANTIC_COMPILER_WERROR": "ON"
}
},
{
"name": "release",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
{
"name": "linux-clang-release",
"displayName": "Linux 64-bit",
"inherits": ["libunicode-common", "release"],
"cacheVariables": {
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "linux-gcc-release",
"displayName": "Linux 64-bit",
"inherits": ["libunicode-common", "release"],
"cacheVariables": {
"CMAKE_CXX_COMPILER": "g++"
}
},
{
"name": "linux-devel-clang-release",
"displayName": "Linux 64-bit",
"inherits": ["libunicode-common", "release", "local-devel"],
"cacheVariables": {
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "linux-devel-gcc-release",
"displayName": "Linux 64-bit",
"inherits": ["libunicode-common", "release", "local-devel"],
"cacheVariables": {
"CMAKE_CXX_COMPILER": "g++"
}
}
],
"buildPresets": [
{ "name": "linux-clang-release", "configurePreset": "linux-clang-release" },
{ "name": "linux-gcc-release", "configurePreset": "linux-gcc-release" },
{ "name": "linux-devel-clang-release", "configurePreset": "linux-devel-clang-release" },
{ "name": "linux-devel-gcc-release", "configurePreset": "linux-devel-gcc-release" }
]
}
13 changes: 13 additions & 0 deletions cmake/ThirdParties.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ if(LIBUNICODE_TESTING)
endif()
endif()

if(LIBUNICODE_BENCHMARK)
if(TARGET benchmark::benchmark_main)
set(THIRDPARTY_BUILTIN_benchmark "embedded")
else()
find_package(benchmark REQUIRED)
set(THIRDPARTY_BUILTIN_benchmark "system package")
endif()
endif()


if(TARGET fmt)
set(THIRDPARTY_BUILTIN_fmt "embedded")
else()
Expand All @@ -57,6 +67,9 @@ macro(ThirdPartiesSummary2)
message(STATUS "------------------------------------------------------------------------------")
if(LIBUNICODE_TESTING)
message(STATUS "Catch2 ${THIRDPARTY_BUILTIN_Catch2}")
endif()
if(LIBUNICODE_BENCHMARK)
message(STATUS "Benchmark ${THIRDPARTY_BUILTIN_benchmark}")
endif()
message(STATUS "fmt ${THIRDPARTY_BUILTIN_fmt}")
message(STATUS "------------------------------------------------------------------------------")
Expand Down
5 changes: 3 additions & 2 deletions scripts/ci-prepare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ prepare_build_ubuntu()
{
cmake \
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \
-DPEDANTIC_COMPILER="ON" \
-DPEDANTIC_COMPILER_WERROR="ON" \
-DPEDANTIC_COMPILER="OFF" \
-DPEDANTIC_COMPILER_WERROR="OFF" \
-DLIBUNICODE_BENCHMARK="ON" \
-S . -B ${BUILD_DIR} \
${EXTRA_CMAKE_FLAGS}
}
Expand Down
13 changes: 12 additions & 1 deletion scripts/install-deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ fetch_and_unpack_Catch2()
https://github.com/catchorg/Catch2/archive/refs/tags/v3.4.0.tar.gz
}

fetch_and_unpack_benchmark()
{
fetch_and_unpack \
benchmark-1.8.3 \
benchmark-1.8.3.tar.gz \
https://github.com/google/benchmark/archive/refs/tags/v1.8.3.tar.gz
}


fetch_and_unpack_fmtlib()
{
fetch_and_unpack \
Expand Down Expand Up @@ -164,7 +173,7 @@ install_deps_arch()
fetch_and_unpack_fmtlib
[ x$PREPARE_ONLY_EMBEDS = xON ] && return

pacman -S -y \
sudo pacman -S -y --needed \
catch2 \
cmake \
git \
Expand Down Expand Up @@ -246,6 +255,8 @@ main()
echo "Dependencies were fetch manually and most likely libunicode will compile."
;;
esac

fetch_and_unpack_benchmark
}

main $*
15 changes: 15 additions & 0 deletions src/libunicode/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ add_library(unicode ${LIBUNICODE_LIB_MODE}
codepoint_properties_names.cpp
)

if(LIBUNICODE_USE_STD_SIMD)
target_compile_definitions(unicode PRIVATE LIBUNICODE_USE_STD_SIMD)
endif()
if(LIBUNICODE_USE_INTRINSICS)
target_compile_definitions(unicode PRIVATE USE_INTRINSICS)
endif()
Expand Down Expand Up @@ -238,3 +241,15 @@ if(LIBUNICODE_TESTING)
add_test(unicode_test unicode_test)
endif()
# }}}



# {{{ unicode_test
if(LIBUNICODE_BENCHMARK)
add_executable(libunicode_benchmark
benchmark.cpp
)
target_compile_features(libunicode_benchmark PRIVATE cxx_std_20)
target_link_libraries(libunicode_benchmark PRIVATE benchmark::benchmark unicode)
endif()
# }}}
67 changes: 67 additions & 0 deletions src/libunicode/benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include <libunicode/convert.h>
#include <libunicode/scan.h>
#include <libunicode/utf8.h>

#include <string_view>

#include <benchmark/benchmark.h>

using std::string_view;

template <size_t L>
static void benchmarkWithLength(benchmark::State& state)
{
auto TestText = std::string(L, 'a') + "\u00A9";
for (auto _: state)
{
benchmark::DoNotOptimize(unicode::detail::scan_for_text_ascii(TestText, L + 10));
}
}

template <size_t L>
static void benchmarkWithOffset(benchmark::State& state)
{
auto TestText = std::string(L, 'a') + "\u0001F600" + std::string(1000, 'a');
for (auto _: state)
{
benchmark::DoNotOptimize(unicode::detail::scan_for_text_ascii(TestText, L + 10));
}
}

BENCHMARK(benchmarkWithLength<1>);
BENCHMARK(benchmarkWithLength<10>);
BENCHMARK(benchmarkWithLength<100>);
BENCHMARK(benchmarkWithLength<1000>);
BENCHMARK(benchmarkWithLength<10000>);
BENCHMARK(benchmarkWithLength<100000>);
BENCHMARK(benchmarkWithLength<1000000>);

BENCHMARK(benchmarkWithOffset<5>);
BENCHMARK(benchmarkWithOffset<10>);
BENCHMARK(benchmarkWithOffset<15>);
BENCHMARK(benchmarkWithOffset<20>);
BENCHMARK(benchmarkWithOffset<25>);
BENCHMARK(benchmarkWithOffset<30>);
BENCHMARK(benchmarkWithOffset<35>);
BENCHMARK(benchmarkWithOffset<40>);
BENCHMARK(benchmarkWithOffset<45>);
BENCHMARK(benchmarkWithOffset<50>);
BENCHMARK(benchmarkWithOffset<55>);
BENCHMARK(benchmarkWithOffset<60>);
BENCHMARK(benchmarkWithOffset<65>);
BENCHMARK(benchmarkWithOffset<70>);
BENCHMARK(benchmarkWithOffset<75>);
BENCHMARK(benchmarkWithOffset<80>);
BENCHMARK(benchmarkWithOffset<85>);
BENCHMARK(benchmarkWithOffset<90>);
BENCHMARK(benchmarkWithOffset<95>);
BENCHMARK(benchmarkWithOffset<100>);
BENCHMARK(benchmarkWithOffset<105>);
BENCHMARK(benchmarkWithOffset<110>);
BENCHMARK(benchmarkWithOffset<115>);
BENCHMARK(benchmarkWithOffset<120>);
BENCHMARK(benchmarkWithOffset<125>);
BENCHMARK(benchmarkWithOffset<130>);

// Run the benchmark
BENCHMARK_MAIN();
29 changes: 27 additions & 2 deletions src/libunicode/scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,22 @@
#include <libunicode/utf8.h>
#include <libunicode/width.h>

#include <iostream>
#include <algorithm>
#include <cassert>
#include <iterator>
#include <numeric>
#include <string_view>

#if defined(__SSE2__)
#if __has_include(<experimental/simd>) && defined(LIBUNICODE_USE_STD_SIMD)
#define USE_STD_SIMD
#include <experimental/simd>
namespace stdx = std::experimental;
#elif __has_include(<simd>) && defined(LIBUNICODE_USE_STD_SIMD)
#define USE_STD_SIMD
#include <simd>
namespace stdx = std;
#elif defined(__SSE2__)
#include <immintrin.h>
#endif

Expand Down Expand Up @@ -80,8 +89,24 @@ size_t detail::scan_for_text_ascii(string_view text, size_t maxColumnCount) noex
{
auto input = text.data();
auto const end = text.data() + min(text.size(), maxColumnCount);
#if defined(USE_STD_SIMD)
constexpr int numberOfElements = stdx::simd_abi::max_fixed_size<char>;
stdx::fixed_size_simd<char, numberOfElements> simd_text {};
while (input < end - numberOfElements)
{
simd_text.copy_from(input, stdx::element_aligned);

#if defined(USE_INTRINSICS)
// check for control
// TODO check for complex
auto const simd_mask_text = (simd_text < 0x20);
if (stdx::popcount(simd_mask_text) > 0)
{
input += stdx::find_first_set(simd_mask_text);
break;
}
input += numberOfElements;
}
#elif defined(USE_INTRINSICS)
intrinsics::m128i const ControlCodeMax = intrinsics::set1_epi8(0x20); // 0..0x1F
intrinsics::m128i const Complex = intrinsics::set1_epi8(-128); // equals to 0x80 (0b1000'0000)

Expand Down

0 comments on commit a051446

Please sign in to comment.