Optimized copying of short lstrings.
The Emscripten build now allows benchmarks to run synchronously.
Оптимизировано копирование коротких lstring.
В Emscripten сборке добавлена возможность запустить бенчмарки синхронно.
This commit is contained in:
Aleksandr Orefkov 2026-02-12 19:47:32 +03:00
parent e62493a678
commit ee73896337
21 changed files with 5164 additions and 5138 deletions

View File

@ -5,7 +5,7 @@ include(FetchContent)
project( project(
simstr simstr
VERSION 1.6.6 VERSION 1.6.7
DESCRIPTION "Yet another modern C++ string library" DESCRIPTION "Yet another modern C++ string library"
HOMEPAGE_URL "https://github.com/orefkov/simstr" HOMEPAGE_URL "https://github.com/orefkov/simstr"
LANGUAGES CXX LANGUAGES CXX

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.6.6 * ver. 1.6.7
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Бенчмарки * Бенчмарки
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -19,10 +19,13 @@ using namespace std::literals;
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
#include <emscripten.h> #include <emscripten.h>
bool do_sync = false;
static void DoTeardown(const benchmark::State& state) { static void DoTeardown(const benchmark::State& state) {
// Это нужно чтобы интерфейс браузера обновился // Это нужно чтобы интерфейс браузера обновился
// This is necessary for the browser interface to refresh. // This is necessary for the browser interface to refresh.
if (!do_sync) {
emscripten_sleep(1); emscripten_sleep(1);
}
} }
#undef BENCHMARK #undef BENCHMARK
#define BENCHMARK(...) \ #define BENCHMARK(...) \
@ -2466,6 +2469,10 @@ int main(int argc, char** argv) {
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
EM_ASM({ console.log(navigator.userAgent); }); EM_ASM({ console.log(navigator.userAgent); });
if (argc == 2 && stra{argv[1]} == "-sync") {
do_sync = true;
argc = 1;
}
#endif #endif
char arg1[] = "--benchmark_repetitions=4", arg2[] = "--benchmark_report_aggregates_only=true"; char arg1[] = "--benchmark_repetitions=4", arg2[] = "--benchmark_report_aggregates_only=true";

View File

@ -147,7 +147,7 @@ However, Windows and Linux are clearly in different weight classes.
- std::string copy{str_with_len_N};/16 - std::string copy{str_with_len_N};/16
Явно виден скачок, где заканчивается SSO и начинается аллокация. Явно виден скачок, где заканчивается SSO и начинается аллокация.
Обратите внимание, что WASM - 32-битный, и там размер Обратите внимание, что WASM - 32-битный, и там размер
SSO у std::string меньше, насколько я помню, 11 символов + 0. SSO у std::string меньше, 10 символов + 0.
The jump where SSO ends and allocation begins is clearly visible. The jump where SSO ends and allocation begins is clearly visible.
Note that WASM is 32-bit, and the size of the SSO for std::string is Note that WASM is 32-bit, and the size of the SSO for std::string is
smaller, as far as I remember: 11 characters + 0. smaller, as far as I remember: 11 characters + 0.

View File

@ -185,7 +185,7 @@
<body> <body>
<div class="head"> <div class="head">
<h3>SimStr 1.6.6 Benchmark</h3> <h3>SimStr 1.6.7 Benchmark</h3>
<span><a href="https://orefkov.github.io/simstr/results.html" target="blank">All results</a></span> <span><a href="https://orefkov.github.io/simstr/results.html" target="blank">All results</a></span>
<span><a href="https://github.com/orefkov/simstr/blob/main/bench/bench_str.cpp" target="blank">Sources for <span><a href="https://github.com/orefkov/simstr/blob/main/bench/bench_str.cpp" target="blank">Sources for
benchmarks</a></span> benchmarks</a></span>
@ -199,25 +199,28 @@
var Module = { var Module = {
print(...args) { print(...args) {
console.log(...args); console.log(...args);
var text = args.join(' ')
try { try {
if (text.match(/^--+[^-]/)) { var text = args.join(' ')
add_benchset(text.match(/^-+ +(.+?) +-+\/repeats/)[1]); var m = text.match(/^--+ +(.+?) +-+\/repeats/);
if (m) {
add_benchset(m[1]);
} else { } else {
var im = text.indexOf("_mean "); var im = text.indexOf("_mean ");
if (im != -1) { if (im != -1) {
add_benchmark(text.substr(0, im), text.match(/ ([^ ]+?) ns/)[1]); add_benchmark(text.substr(0, im), text.match(/ ([^ ]+?) ns/)[1]);
} }
} }
outputElement.value += text + "\n";
outputElement.scrollTop = outputElement.scrollHeight;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
if (outputElement) {
outputElement.value += text + "\n";
outputElement.scrollTop = outputElement.scrollHeight;
}
} }
}; };
var sync = new URLSearchParams(window.location.search).get('sync');
if (sync)
Module.arguments = ["-sync"];
function on_done() { function on_done() {
resultEl.scrollTop = 0; resultEl.scrollTop = 0;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.6.6 PROJECT_NUMBER = 1.6.7
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a # for a project that appears at the top of each page and should give viewers a

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.6.6 PROJECT_NUMBER = 1.6.7
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a # for a project that appears at the top of each page and should give viewers a

View File

@ -1,6 +1,6 @@
/* /*
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* ver. 1.6.6 * ver. 1.6.7
*/ */
#pragma once #pragma once

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.6.6 * ver. 1.6.7
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Классы для работы со строками * Классы для работы со строками
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -2661,7 +2661,13 @@ protected:
size_ = newSize; size_ = newSize;
data_[newSize] = 0; data_[newSize] = 0;
} }
constexpr void copy_from_another(K* buf, const K* src, size_t size) {
// Реальный размер буфера всегда кратен sizeof(void*), поэтому копируя по sizeof(void*) байтов, мы не выйдем за пределы буфера
// The actual buffer size is always a multiple of sizeof(void*), so by copying sizeof(void*) bytes at a time, we won't go beyond the buffer's limits.
size_t need_copy_bytes = (size + 1) * sizeof(K);
size_t cnt = (need_copy_bytes + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*);
traits::copy(buf, src, cnt / sizeof(K));
}
public: public:
/*! /*!
* @ru @brief Создать пустой объект. * @ru @brief Создать пустой объект.
@ -2776,16 +2782,21 @@ public:
* @param other - another string. * @param other - another string.
*/ */
constexpr lstring(const my_type& other) : base_storable(other.allocator()) { constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
if (other.size_) { struct copy{uint64_t p[2];};
K* buf = init(other.size_); constexpr size_t short_str = sizeof(copy) / sizeof(K);
const size_t short_str = 16 / sizeof(K); if constexpr (LocalCapacity >= short_str - 1) {
if (LocalCapacity >= short_str - 1 && other.size_ < short_str) { if (other.size_ < short_str) {
struct copy { char buf[16]; }; data_ = local_;
*reinterpret_cast<copy*>(buf) = *reinterpret_cast<const copy*>(other.symbols()); size_ = other.size_;
} else { *(copy*)local_ = *(const copy*)other.local_;
traits::copy(buf, other.symbols(), other.size_ + 1); return;
} }
} }
if (size_t size = other.size_) {
copy_from_another(init(size), other.symbols(), size);
} else {
create_empty();
}
} }
/*! /*!
* @ru @brief Копирование из другой строки такого же типа, но с другим аллокатором. * @ru @brief Копирование из другой строки такого же типа, но с другим аллокатором.
@ -2799,7 +2810,9 @@ public:
requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>) requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) { constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
if (other.size_) { if (other.size_) {
traits::copy(init(other.size_), other.symbols(), other.size_ + 1); copy_from_another(init(other.size_), other.symbols(), other.size_);
} else {
create_empty();
} }
} }
/*! /*!
@ -2816,10 +2829,11 @@ public:
if constexpr (I > 1) { if constexpr (I > 1) {
K* ptr = init(I - 1); K* ptr = init(I - 1);
traits::copy(ptr, (const K*)value, I - 1); traits::copy(ptr, (const K*)value, I - 1);
ptr[I - 1] = 0; ptr[I - 1] = K{};
} else } else {
create_empty(); create_empty();
} }
}
/*! /*!
* @ru @brief Конструктор перемещения из строки такого же типа. * @ru @brief Конструктор перемещения из строки такого же типа.
* @param other - другая строка. * @param other - другая строка.
@ -2831,14 +2845,16 @@ public:
size_ = other.size_; size_ = other.size_;
if (other.is_alloced()) { if (other.is_alloced()) {
data_ = other.data_; data_ = other.data_;
other.data_ = other.local_;
capacity_ = other.capacity_; capacity_ = other.capacity_;
} else { } else {
data_ = local_; data_ = local_;
traits::copy(local_, other.local_, size_ + 1); copy_from_another(data_, other.local_, size_);
} }
other.data_ = other.local_;
other.size_ = 0; other.size_ = 0;
other.local_[0] = 0; other.local_[0] = 0;
} else {
create_empty();
} }
} }
/*! /*!

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.6.6 * ver. 1.6.7
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* База для строковых конкатенаций через выражения времени компиляции * База для строковых конкатенаций через выражения времени компиляции
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com

View File

@ -3,7 +3,7 @@
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml) [![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Version 1.6.6. Version 1.6.7.
<h2>Speed up your work with strings by 2-10 times!</h2> <h2>Speed up your work with strings by 2-10 times!</h2>
@ -323,8 +323,8 @@ function(add_simstr)
simstr simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
GIT_TAG tags/rel1.6.6 # Specify the desired release GIT_TAG tags/rel1.6.7 # Specify the desired release
FIND_PACKAGE_ARGS NAMES simstr 1.6.6 FIND_PACKAGE_ARGS NAMES simstr 1.6.7
) )
FetchContent_MakeAvailable(simstr) FetchContent_MakeAvailable(simstr)
endfunction() endfunction()

View File

@ -3,7 +3,7 @@
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml) [![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Версия 1.6.6. Версия 1.6.7.
<h2>Ускорь работу со строками в 2-10 раз!</h2> <h2>Ускорь работу со строками в 2-10 раз!</h2>
@ -323,8 +323,8 @@ function(add_simstr)
simstr simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
GIT_TAG tags/rel1.6.6 # Укажите нужный релиз GIT_TAG tags/rel1.6.7 # Укажите нужный релиз
FIND_PACKAGE_ARGS NAMES simstr 1.6.6 FIND_PACKAGE_ARGS NAMES simstr 1.6.7
) )
FetchContent_MakeAvailable(simstr) FetchContent_MakeAvailable(simstr)
endfunction() endfunction()

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.6.6 * ver. 1.6.7
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Реализация строковых функций * Реализация строковых функций
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.6.6 * ver. 1.6.7
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr * Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.6.6 * ver. 1.6.7
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr * Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.6.6 * ver. 1.6.7
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr * Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com