Skip to content

Fix MinGW Clang DLL build while preserving thread-local caching - #1990

Merged
an-tao merged 3 commits into
drogonframework:masterfrom
StableAgOH:remove-thread_local
Oct 30, 2026
Merged

an-tao merged 3 commits into
drogonframework:masterfrom
StableAgOH:remove-thread_local

Conversation

@StableAgOH

Copy link
Copy Markdown
Contributor

Fix #1845.

@StableAgOH

Copy link
Copy Markdown
Contributor Author

@an-tao CI was passed in my repository. Request for re-run.

@an-tao
an-tao requested a review from marty1885 May 10, 2025 12:56
@marty1885

Copy link
Copy Markdown
Member

@StableAgOH Can you benchmark the performance difference? The reason for thread_local was to buffer some randomness generated from the random API. Random can have a high overhead per call. thread_local allows extra randomness be reused. (static does not work as the random function must be thread safe)

@Nahida1O27

Nahida1O27 commented Oct 29, 2026 •

Copy link
Copy Markdown

@an-tao @marty1885 我做了benchmark。
Hello, recently I’ve finished packaging Drogon for MSYS2. Today I do benchmark on my Win11 PC.
Below info to refer:

  • CPU: i5-1155G7
  • OS: Windows 10.0.26300.9711
  • compiler: clang++ 23.1.3 (aka也就是mingw后端的llvm)
  • target: x86_64-w64-windows-gnu
  • thread model: posix
  • Trantor version: 1.5.29

测试源码:

// test.cc
//静态链接trantor,动态链接benchmark
//static link against libtrantor.a, dynamic link against libbenchmark.dll
#define TRANTOR_STATIC_DEFINE

#include <cassert>
#include <trantor/utils/Utilities.h>
#include <benchmark/benchmark.h>

namespace A {
namespace B {
std::string former(size_t size) { /* thread_local */ }

std::string later(size_t size) {/* this PR change */}
}   // namespace B
}   // namespace A

static void BM_former(benchmark::State& state) {
    for(auto _: state) {
        auto s = A::B::former(static_cast<size_t>(state.range(0)));
        benchmark::DoNotOptimize(s);
    }
}
static void BM_later(benchmark::State& state) {
    for(auto _: state) {
        auto s = A::B::later(static_cast<size_t>(state.range(0)));
        benchmark::DoNotOptimize(s);
    }
}


BENCHMARK(BM_former)->Arg(16)->Arg(64)->Arg(256)->Arg(1024);
BENCHMARK(BM_later)->Arg(16)->Arg(64)->Arg(256)->Arg(1024);
BENCHMARK_MAIN(); 

输出结果result:

Glarboy@LAPTOP-KMLVGRGJ CLANG64 D:/Glarboy/test/build
$ ./test.exe --benchmark_min_time=1.0
2026-10-29T22:04:47+08:00
Running D:\Glarboy\test\build\test.exe
Run on (8 X 2496 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x4)
  L1 Instruction 32 KiB (x4)
  L2 Unified 1280 KiB (x4)
  L3 Unified 8192 KiB (x1)
-- LOG(0): Value passed to --benchmark_min_time should have a suffix. Eg., `30s` for 30-seconds.
---------------------------------------------------------
Benchmark               Time             CPU   Iterations
---------------------------------------------------------
BM_former/16          167 ns          165 ns      8145455
BM_former/64          718 ns          722 ns      1991111
BM_former/256        2805 ns         2668 ns       527059
BM_former/1024      10654 ns        10781 ns       100000
BM_later/16           301 ns          302 ns      4715789
BM_later/64           739 ns          738 ns      1991111
BM_later/256         2607 ns         2579 ns       527059
BM_later/1024       11224 ns        11108 ns       128000
Glarboy@LAPTOP-KMLVGRGJ CLANG64 D:/Glarboy/test/build
$ ./test.exe --benchmark_min_time=10.0
2026-10-29T22:09:15+08:00
Running D:\Glarboy\test\build\test.exe
Run on (8 X 2496 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x4)
  L1 Instruction 32 KiB (x4)
  L2 Unified 1280 KiB (x4)
  L3 Unified 8192 KiB (x1)
-- LOG(0): Value passed to --benchmark_min_time should have a suffix. Eg., `30s` for 30-seconds.
---------------------------------------------------------
Benchmark               Time             CPU   Iterations
---------------------------------------------------------
BM_former/16          170 ns          167 ns     81454545
BM_former/64          739 ns          737 ns     19352052
BM_former/256        2836 ns         2827 ns      5239766
BM_former/1024      12931 ns        12610 ns      1280000
BM_later/16           320 ns          319 ns     44577114
BM_later/64           764 ns          764 ns     17164751
BM_later/256         2857 ns         2830 ns      5090909
BM_later/1024       10566 ns        10535 ns      1280000

@Nahida1O27

Copy link
Copy Markdown

看起来生成短字符更慢了

@StableAgOH StableAgOH changed the title Remove thread_local in dllexported function Fix MinGW Clang DLL build while preserving thread-local caching Oct 30, 2026
@StableAgOH

Copy link
Copy Markdown
Contributor Author

看起来生成短字符更慢了

Thanks for the benchmark. I've updated this PR with a smaller fix: moving the TLS declarations outside the lambda while preserving cross-call caching.

I compared all three implementations on Windows 11, Ryzen 7 9800X3D, Clang 23.1.3 (MSYS2 UCRT64), using Trantor 1.5.29 with OpenSSL 3.6.5 and Google Benchmark 1.10.0 (-O3 -DNDEBUG, no LTO).

Single-thread median wall-clock time over 7 randomly interleaved repetitions:

Length Original Previous PR Updated PR
16 164 ns 285 ns 157 ns
32 306 ns 293 ns 288 ns
33 316 ns 539 ns 310 ns
64 573 ns 558 ns 559 ns
256 2118 ns 1992 ns 2063 ns
1024 7971 ns 7810 ns 7628 ns

The updated version avoids the short-string regression seen in the previous PR version.

@an-tao
an-tao merged commit ad9d64d into drogonframework:master Oct 30, 2026
62 of 63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Does not build as shared library

4 participants