-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Use sync.Map and atomics to improve sum performance #7427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use sync.Map and atomics to improve sum performance #7427
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7427 +/- ##
======================================
Coverage 85.7% 85.7%
======================================
Files 285 286 +1
Lines 25228 25331 +103
======================================
+ Hits 21633 21731 +98
- Misses 3216 3219 +3
- Partials 379 381 +2
🚀 New features to boost your workflow:
|
c2e13c0 to
9b7c71a
Compare
|
Feel free to review. I tried to document the code as well as I could. I plan to present this at the 10/2 SIG meeting so it might be easier to review after that. |
9b7c71a to
b8772f7
Compare
Fixes #7388 Benchmarks seem like mostly noise. It isn't actually necessary to lock in the exemplar reservoir today because of our SDK design, but this allows us to make optimizations in the future. After #7427, improvements to exemplar reservoir locking will greatly improve the ExemplarEnabled benchmarks. Parallel benchmarks: ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/metric cpu: Intel(R) Xeon(R) CPU @ 2.20GHz │ main24.txt │ exemplar24.txt │ │ sec/op │ sec/op vs base │ SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/0-24 399.5n ± 16% 367.8n ± 17% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/1-24 369.4n ± 27% 410.2n ± 11% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/10-24 372.6n ± 23% 398.9n ± 8% ~ (p=0.394 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/0-24 313.4n ± 12% 357.7n ± 20% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/1-24 389.9n ± 12% 379.5n ± 9% ~ (p=0.818 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/10-24 441.4n ± 13% 359.1n ± 18% -18.64% (p=0.009 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/0-24 415.8n ± 22% 400.3n ± 16% ~ (p=0.937 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/1-24 346.9n ± 8% 364.6n ± 19% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/10-24 358.9n ± 14% 407.1n ± 17% ~ (p=0.093 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/0-24 381.9n ± 27% 375.2n ± 10% ~ (p=0.937 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/1-24 361.9n ± 19% 389.6n ± 23% ~ (p=0.818 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/10-24 356.0n ± 8% 416.1n ± 14% +16.90% (p=0.015 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/0-24 313.9n ± 11% 385.7n ± 19% +22.88% (p=0.041 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/1-24 368.8n ± 18% 387.6n ± 17% ~ (p=0.394 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/10-24 346.1n ± 40% 460.3n ± 16% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/0-24 325.9n ± 10% 357.8n ± 19% +9.77% (p=0.026 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/1-24 372.1n ± 18% 395.2n ± 14% ~ (p=0.180 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/10-24 353.5n ± 23% 416.0n ± 15% +17.66% (p=0.024 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/0-24 351.8n ± 15% 362.7n ± 7% ~ (p=0.699 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/1-24 378.8n ± 17% 413.4n ± 13% ~ (p=0.288 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/10-24 361.5n ± 13% 418.2n ± 14% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/0-24 305.0n ± 21% 361.9n ± 13% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/1-24 411.1n ± 12% 403.9n ± 9% ~ (p=0.937 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/10-24 353.4n ± 39% 380.9n ± 17% ~ (p=0.394 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/0-24 457.5n ± 33% 454.2n ± 13% ~ (p=1.000 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/1-24 436.6n ± 23% 459.0n ± 10% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/10-24 383.3n ± 22% 461.9n ± 12% +20.51% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/0-24 371.9n ± 14% 421.4n ± 19% +13.33% (p=0.004 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/1-24 433.7n ± 20% 490.8n ± 10% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/10-24 433.2n ± 18% 511.4n ± 9% +18.05% (p=0.041 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/0-24 477.5n ± 14% 384.6n ± 7% -19.47% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/1-24 481.1n ± 17% 430.0n ± 18% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/10-24 425.1n ± 27% 436.3n ± 12% ~ (p=0.699 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/0-24 394.5n ± 8% 415.5n ± 15% ~ (p=0.589 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/1-24 434.4n ± 13% 440.8n ± 15% ~ (p=0.937 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/10-24 481.3n ± 19% 404.1n ± 14% -16.05% (p=0.009 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/0-24 364.9n ± 29% 424.3n ± 8% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/1-24 401.2n ± 24% 482.2n ± 12% +20.20% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/10-24 438.6n ± 19% 404.8n ± 18% ~ (p=0.485 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/0-24 392.7n ± 17% 427.7n ± 25% ~ (p=0.180 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/1-24 392.6n ± 5% 388.5n ± 7% ~ (p=0.818 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/10-24 401.3n ± 19% 409.7n ± 8% ~ (p=0.818 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/0-24 369.8n ± 15% 374.2n ± 17% ~ (p=0.818 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/1-24 359.4n ± 13% 387.1n ± 16% ~ (p=0.180 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/10-24 393.2n ± 18% 450.0n ± 10% +14.43% (p=0.015 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/0-24 399.8n ± 23% 361.2n ± 11% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/1-24 439.4n ± 25% 412.0n ± 10% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/10-24 401.7n ± 17% 380.0n ± 11% ~ (p=0.394 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/0-24 508.8n ± 18% 532.6n ± 16% ~ (p=0.937 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/1-24 505.9n ± 22% 494.8n ± 14% ~ (p=0.589 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/10-24 597.8n ± 10% 490.5n ± 23% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/0-24 566.2n ± 21% 482.9n ± 10% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/1-24 440.2n ± 9% 549.3n ± 8% +24.77% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/10-24 436.3n ± 16% 530.8n ± 19% ~ (p=0.093 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/0-24 395.8n ± 25% 441.6n ± 9% ~ (p=0.065 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/1-24 440.2n ± 9% 455.2n ± 7% ~ (p=0.180 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/10-24 415.7n ± 12% 527.5n ± 9% +26.91% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/0-24 376.1n ± 19% 461.9n ± 13% +22.81% (p=0.009 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/1-24 383.6n ± 10% 422.7n ± 26% +10.21% (p=0.015 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/10-24 398.5n ± 9% 538.5n ± 7% +35.13% (p=0.002 n=6) geomean 399.4n 422.3n +5.72% ``` Single-threaded benchmarks: ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/metric cpu: Intel(R) Xeon(R) CPU @ 2.20GHz │ main1.txt │ exemplar1.txt │ │ sec/op │ sec/op vs base │ SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/0 180.1n ± 21% 156.2n ± 10% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/1 168.4n ± 9% 179.2n ± 11% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/10 164.0n ± 10% 199.0n ± 20% +21.30% (p=0.004 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/0 153.9n ± 6% 170.1n ± 2% +10.53% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/1 178.0n ± 5% 178.5n ± 6% ~ (p=0.818 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/10 175.8n ± 7% 165.3n ± 18% ~ (p=0.589 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/0 152.0n ± 8% 163.1n ± 26% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/1 168.1n ± 8% 168.0n ± 14% ~ (p=0.818 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/10 167.4n ± 4% 164.3n ± 12% ~ (p=0.699 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/0 151.7n ± 17% 156.0n ± 28% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/1 173.6n ± 5% 169.3n ± 5% -2.45% (p=0.041 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/10 169.3n ± 4% 165.7n ± 7% ~ (p=0.394 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/0 155.5n ± 15% 153.8n ± 11% ~ (p=0.558 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/1 166.7n ± 3% 173.5n ± 7% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/10 174.4n ± 17% 167.6n ± 13% ~ (p=0.699 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/0 180.1n ± 32% 154.8n ± 5% -14.02% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/1 204.6n ± 23% 173.8n ± 19% ~ (p=0.069 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/10 226.6n ± 28% 174.5n ± 10% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/0 132.7n ± 13% 140.7n ± 14% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/1 143.4n ± 9% 162.6n ± 9% +13.42% (p=0.004 n=6) SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/10 154.7n ± 7% 172.3n ± 6% +11.38% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/0 136.8n ± 10% 145.5n ± 14% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/1 148.4n ± 11% 159.7n ± 8% ~ (p=0.167 n=6) SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/10 193.0n ± 25% 165.5n ± 9% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/0 249.8n ± 47% 229.5n ± 7% ~ (p=0.394 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/1 262.1n ± 20% 245.1n ± 12% ~ (p=0.699 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/10 285.4n ± 20% 249.9n ± 17% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/0 272.9n ± 20% 216.3n ± 6% -20.75% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/1 311.8n ± 29% 234.4n ± 6% ~ (p=0.132 n=6) SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/10 228.2n ± 6% 234.2n ± 4% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/0 289.2n ± 39% 263.3n ± 10% ~ (p=1.000 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/1 271.9n ± 18% 280.6n ± 8% ~ (p=0.589 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/10 272.1n ± 6% 303.7n ± 14% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/0 280.1n ± 8% 268.8n ± 5% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/1 291.6n ± 81% 268.8n ± 7% ~ (p=0.180 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/10 276.3n ± 13% 278.1n ± 6% ~ (p=0.784 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/0 254.7n ± 7% 282.4n ± 5% +10.85% (p=0.026 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/1 277.5n ± 11% 285.2n ± 16% ~ (p=0.937 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/10 267.0n ± 10% 275.9n ± 5% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/0 256.9n ± 3% 286.4n ± 8% +11.46% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/1 267.9n ± 16% 278.0n ± 12% ~ (p=0.180 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/10 272.5n ± 4% 267.4n ± 6% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/0 272.5n ± 22% 266.2n ± 14% ~ (p=0.589 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/1 355.2n ± 11% 275.2n ± 7% -22.52% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/10 281.3n ± 7% 268.9n ± 4% ~ (p=0.093 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/0 257.0n ± 9% 308.9n ± 7% +20.20% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/1 265.9n ± 9% 319.6n ± 7% +20.18% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/10 279.9n ± 3% 332.9n ± 7% +18.92% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/0 313.9n ± 11% 345.1n ± 10% +9.91% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/1 332.1n ± 7% 359.9n ± 9% +8.37% (p=0.009 n=6) SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/10 366.4n ± 55% 380.1n ± 4% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/0 336.8n ± 12% 346.3n ± 7% ~ (p=0.589 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/1 361.9n ± 18% 375.0n ± 8% +3.62% (p=0.004 n=6) SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/10 351.8n ± 6% 392.1n ± 4% +11.46% (p=0.002 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/0 358.8n ± 9% 344.3n ± 5% ~ (p=0.310 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/1 352.0n ± 7% 331.5n ± 5% -5.84% (p=0.041 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/10 373.5n ± 10% 353.7n ± 9% ~ (p=0.394 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/0 328.5n ± 9% 345.4n ± 5% +5.14% (p=0.015 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/1 349.1n ± 28% 337.8n ± 9% ~ (p=0.240 n=6) SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/10 347.5n ± 10% 344.2n ± 10% ~ (p=1.000 n=6) geomean 235.3n 234.4n -0.38% ```
b8772f7 to
1354e22
Compare
|
This is ready for review, but tests will fail until it is rebased on #7441 |
bf222ff to
aad9961
Compare
d490cc4 to
6bb5780
Compare
6bb5780 to
b71918b
Compare
b71918b to
738a764
Compare
9dda27f to
e636fe3
Compare
e636fe3 to
5485c5b
Compare
Alternative to #7380
This uses a sync.Map and atomics for the sum's counter value. This intentionally introduces a new race condition that didn't previously exist:
Alternatives Considered
RWLock for the map instead of sync.Map
This is significantly less performant.
Single sync.Map without hotColdWaitGroup
Deleting keys from the sync.Map concurrently with measurements (during Clear() of the sync.Map) can cause measurements to be made to a counter that has already been read, exported and deleted. This can produce incorrect sums when delta is used. Instead, atomically switching writes to a completely empty sync.Map and waiting for writes to the previous sync.Map complete eliminates this issue.
Use two sync.Map for cumulative sums
One idea I explored was doing a hot-cold swap for cumulative sums just like we do for delta sums. We would swap the hot and cold sync.Maps, wait for writes to the cold sync.Map to complete while new writes go to the hot map. Then, once we are done reading the cold map, we could merge the contents of the cold map back into the new hot map.
This approach has two issues:
Benchmarks
Parallel:
Single-threaded: