Skip to content

Commit 8447609

Browse files
committed
Builds with thread_sanitizer
1 parent 3f18117 commit 8447609

File tree

4 files changed

+78
-1
lines changed

4 files changed

+78
-1
lines changed

Benchmarks.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,69 @@ As usual for microbenchmarks, take them with a grain of salt.
306306
| 1,000 | 8,722.72 | 114,643.08 | 0.1% | 1.18 | `guardedvector.push_back_noguard`
307307
| 1,000 | 9,891.90 | 101,092.79 | 0.1% | 1.19 | `guardedvector.push_back`
308308

309+
## ThreadSanitizer
310+
311+
### Clang 14.0.6 WSL `-fsanitize=thread -O3 -DNDEBUG` (CMake Release default)
312+
313+
| complexityN | ns/op | op/s | err% | total | Vector of uint64_t
314+
|------------:|--------------------:|--------------------:|--------:|----------:|:-------------------
315+
| 1,000 | 27,569.43 | 36,272.06 | 0.1% | 1.21 | `std::vector.push_back`
316+
| 100,000 | 2,755,273.56 | 362.94 | 0.1% | 1.21 | `std::vector.push_back`
317+
| 10,000,000 | 275,502,920.00 | 3.63 | 0.0% | 3.18 | `std::vector.push_back`
318+
| 1,000 | 34,961.04 | 28,603.27 | 0.0% | 1.21 | `std::vector.push_back - noreserve`
319+
| 100,000 | 6,557,961.12 | 152.49 | 0.1% | 1.22 | `std::vector.push_back - noreserve`
320+
| 10,000,000 | 873,837,301.00 | 1.14 | 3.2% | 9.30 | `std::vector.push_back - noreserve`
321+
322+
| complexityN | ns/op | op/s | err% | total | Vector of uint64_t*2
323+
|------------:|--------------------:|--------------------:|--------:|----------:|:---------------------
324+
| 1,000 | 33,820.31 | 29,568.03 | 0.1% | 1.22 | `std::vector.push_back`
325+
| 100,000 | 3,385,774.65 | 295.35 | 0.1% | 1.21 | `std::vector.push_back`
326+
| 10,000,000 | 338,732,175.00 | 2.95 | 0.1% | 4.15 | `std::vector.push_back`
327+
328+
| complexityN | ns/op | op/s | err% | total | Vector of uint64_t*4
329+
|------------:|--------------------:|--------------------:|--------:|----------:|:---------------------
330+
| 1,000 | 54,151.45 | 18,466.73 | 0.0% | 1.21 | `std::vector.push_back`
331+
| 100,000 | 5,437,534.41 | 183.91 | 0.2% | 1.22 | `std::vector.push_back`
332+
| 10,000,000 | 543,613,425.00 | 1.84 | 0.1% | 6.57 | `std::vector.push_back`
333+
334+
| complexityN | ns/op | op/s | err% | total | Vector of std::string
335+
|------------:|--------------------:|--------------------:|--------:|----------:|:----------------------
336+
| 1,000 | 48,015.64 | 20,826.55 | 0.1% | 1.21 | `std::vector.push_back`
337+
| 100,000 | 4,812,346.95 | 207.80 | 0.1% | 1.23 | `std::vector.push_back`
338+
| 10,000,000 | 483,697,656.00 | 2.07 | 0.1% | 5.90 | `std::vector.push_back`
339+
340+
341+
### Clang 14.0.6 WSL `-fsanitize=thread -O2 -DNDEBUG` (CMake RelWithDebInfo default)
342+
343+
344+
| complexityN | ns/op | op/s | err% | total | Vector of uint64_t
345+
|------------:|--------------------:|--------------------:|--------:|----------:|:-------------------
346+
| 1,000 | 27,600.31 | 36,231.48 | 0.1% | 1.21 | `std::vector.push_back`
347+
| 100,000 | 2,756,755.84 | 362.75 | 0.1% | 1.21 | `std::vector.push_back`
348+
| 10,000,000 | 275,798,601.00 | 3.63 | 0.1% | 3.18 | `std::vector.push_back`
349+
| 1,000 | 35,054.95 | 28,526.64 | 0.0% | 1.21 | `std::vector.push_back - noreserve`
350+
| 100,000 | 7,808,870.40 | 128.06 | 0.2% | 1.18 | `std::vector.push_back - noreserve`
351+
| 10,000,000 | 888,449,152.00 | 1.13 | 1.1% | 9.83 | `std::vector.push_back - noreserve`
352+
353+
| complexityN | ns/op | op/s | err% | total | Vector of uint64_t*2
354+
|------------:|--------------------:|--------------------:|--------:|----------:|:---------------------
355+
| 1,000 | 65,370.03 | 15,297.53 | 0.1% | 1.16 | `std::vector.push_back`
356+
| 100,000 | 6,549,550.81 | 152.68 | 0.2% | 1.14 | `std::vector.push_back`
357+
| 10,000,000 | 653,770,052.00 | 1.53 | 0.0% | 7.47 | `std::vector.push_back`
358+
359+
| complexityN | ns/op | op/s | err% | total | Vector of uint64_t*4
360+
|------------:|--------------------:|--------------------:|--------:|----------:|:---------------------
361+
| 1,000 | 91,220.54 | 10,962.44 | 0.2% | 1.19 | `std::vector.push_back`
362+
| 100,000 | 9,141,664.85 | 109.39 | 0.1% | 1.23 | `std::vector.push_back`
363+
| 10,000,000 | 914,898,255.00 | 1.09 | 0.1% | 10.89 | `std::vector.push_back`
364+
365+
| complexityN | ns/op | op/s | err% | total | Vector of std::string
366+
|------------:|--------------------:|--------------------:|--------:|----------:|:----------------------
367+
| 1,000 | 91,597.87 | 10,917.28 | 0.0% | 1.19 | `std::vector.push_back`
368+
| 100,000 | 9,182,358.45 | 108.90 | 0.1% | 1.23 | `std::vector.push_back`
369+
| 10,000,000 | 920,280,188.00 | 1.09 | 0.1% | 10.69 | `std::vector.push_back`
370+
371+
309372
## Summary
310373

311374
- Release builds
@@ -327,6 +390,8 @@ As usual for microbenchmarks, take them with a grain of salt.
327390
This seems to be a totally acceptable overhead in most cases given the chances it has to detect issues.
328391
Any object containing the equivalent of two pointers will most likely see only a small decrease in performance for `push_back`.
329392

393+
Compared to thread sanitizer, this is 5 to 30 times faster. Of course the detection level is way lower, but this is great as a smoketest since your code runs a lot during development.
394+
330395
On games for which we tested the guards, less than 2% of regression in frame duration was observed. Which makes sense, since you do not (rather, should not) spend most of your time doing operations on containers.
331396

332397
However, I would still recommend disabling the guards in production.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ As a bonus, we also get detection of memory use-after-free and corruption for fr
1313
- Teams working on real-time/legacy projects
1414
- Teams working with concurrency a lot
1515
- Especially with frequent onboarding of newcomers
16+
- Toolchain vendors that would like to propose a lighter version of ThreadSanitizer shadow algorithm
1617

1718
# Goals/Features
1819

@@ -206,6 +207,8 @@ See [Benchmarks.md](Benchmarks.md)
206207
This seems to be a totally acceptable overhead in most cases given the chances it has to detect issues.
207208
Any object containing the equivalent of two pointers will most likely see only a small decrease in performance for `push_back`.
208209

210+
Compared to thread sanitizer, this is 5 to 30 times faster. Of course the detection level is way lower, but this is great as a smoketest since your code runs a lot during development.
211+
209212
On games for which we tested the guards, less than 2% of regression in frame duration was observed. Which makes sense, since you do not (rather, should not) spend most of your time doing operations on containers.
210213

211214
However, I would still recommend disabling the guards in production.

benchmarks/BenchGuardedVectorExample.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ int BenchVector(ankerl::nanobench::Bench& bench, bool withNoReserve)
3535
}
3636
}
3737

38+
#if defined(__has_feature) && __has_feature(thread_sanitizer)
3839
#ifndef NDEBUG // Only gives different perf in debug builds
3940
{
4041
ExampleGuardedVector<T> guardedvector;
@@ -72,7 +73,7 @@ int BenchVector(ankerl::nanobench::Bench& bench, bool withNoReserve)
7273
});
7374
}
7475
}
75-
76+
#endif //defined(__has_feature) && __has_feature(thread_sanitizer)
7677
if (withNoReserve)
7778
{
7879
{
@@ -93,6 +94,7 @@ int BenchVector(ankerl::nanobench::Bench& bench, bool withNoReserve)
9394
}
9495
}
9596

97+
#if defined(__has_feature) && __has_feature(thread_sanitizer)
9698
{
9799
ExampleGuardedVector<T> guardedvector;
98100
for (size_t size : nbPushBacksPerIteration)
@@ -111,6 +113,7 @@ int BenchVector(ankerl::nanobench::Bench& bench, bool withNoReserve)
111113
}
112114
}
113115
}
116+
#endif //defined(__has_feature) && __has_feature(thread_sanitizer)
114117
return x;
115118
}
116119

src/BadAccessGuards.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
# endif
1111
#endif
1212

13+
// Disable the guards when running ThreadSanitizer, we are racy by design!
14+
#if defined(__has_feature) && __has_feature(thread_sanitizer)
15+
# undef BAD_ACCESS_GUARDS_ENABLE
16+
# define BAD_ACCESS_GUARDS_ENABLE 0
17+
#endif
18+
1319
#if BAD_ACCESS_GUARDS_ENABLE
1420

1521
#include <stdint.h>

0 commit comments

Comments
 (0)