Files
Etienne Perot 7673c0e94b systrap: Eliminate false sharing in contextQueue struct fields.
Any writes invalidates the cache line for other threads, so:

- Group fields by writer (Sentry-written, stub-written, both-written)
- Add padding between groups
- Ensure `fastPathDisabled` writes occur only for real bit flips.

Also add tests to ensure the context queue fields align across languages.

Memory usage unchanged. +348 bytes padding, doesn't change number of pages.

Benchmarks:

```
                                                              │    before    │                after                 │
                                                              │    sec/op    │    sec/op     vs base                │
Syscallbench/syscall.getpid-20                                  2.255µ ±  0%   2.255µ ±  0%        ~ (p=1.000 n=16)
Hackbench/ipcMode.pipe/processMode.thread-20                    4.180m ±  0%   3.446m ±  8%  -17.57% (p=0.000 n=8)
Redis/operation.SET-20                                          27.97µ ±  3%   27.77µ ±  2%        ~ (p=0.809 n=16)
Redis/operation.LPUSH-20                                        28.08µ ±  3%   27.66µ ±  4%        ~ (p=0.108 n=16)
Redis/operation.LRANGE_100-20                                   46.25µ ±  2%   46.35µ ±  1%        ~ (p=0.817 n=16)
Ffmpeg-20                                                        25.22 ±  1%    25.26 ±  1%        ~ (p=0.752 n=16)
IperfOneConnection/operation.Upload-20                          2.723µ ± 35%   1.788µ ± 55%        ~ (p=0.274 n=16)
IperfOneConnection/operation.Download-20                        2.160µ ± 25%   2.134µ ± 23%        ~ (p=0.724 n=16)
IperfManyConnections/operation.Upload/parallel.16-20            2.677µ ±  1%   2.668µ ±  1%        ~ (p=0.596 n=16)
IperfManyConnections/operation.Download/parallel.16-20          2.618µ ±  2%   2.592µ ±  1%   -0.99% (p=0.048 n=16)
NginxDocSize/filesize.1Kb/concurrency.100/filesystem.tmpfs-20   31.44µ ±  2%   30.73µ ±  1%   -2.25% (p=0.023 n=16)
NginxDocSize/filesize.1Kb/concurrency.1/filesystem.tmpfs-20     161.6µ ±  2%   159.0µ ±  3%        ~ (p=0.491 n=16)
Ruby/concurrency.25-20                                          192.7µ ±  1%   192.0µ ±  2%        ~ (p=0.642 n=16)
geomean                                                         64.70µ         61.31µ         -5.24%

                                             │      before      │                  after                  │
                                             │ execution_time.s │ execution_time.s  vs base               │
Hackbench/ipcMode.pipe/processMode.thread-20         7.703 ± 2%         6.232 ± 6%  -19.08% (p=0.000 n=8)

                                             │          before           │                      after                       │
                                             │ execution_time_per_loop.s │ execution_time_per_loop.s  vs base               │
Hackbench/ipcMode.pipe/processMode.thread-20                 3.851m ± 2%                 3.116m ± 6%  -19.09% (p=0.000 n=8)

                                                       │    before     │                 after                 │
                                                       │      B/s      │      B/s       vs base                │
IperfOneConnection/operation.Upload-20                   44.84Gi ± 54%   68.30Gi ± 35%        ~ (p=0.270 n=16)
IperfOneConnection/operation.Download-20                 59.25Gi ± 25%   59.78Gi ± 25%        ~ (p=0.724 n=16)
IperfManyConnections/operation.Upload/parallel.16-20     45.60Gi ±  1%   45.75Gi ±  1%        ~ (p=0.590 n=16)
IperfManyConnections/operation.Download/parallel.16-20   46.63Gi ±  2%   47.10Gi ±  1%        ~ (p=0.051 n=16)
geomean                                                  48.75Gi         54.46Gi        +11.71%

                                                       │           before           │                       after                       │
                                                       │ bandwidth.bytes_per_second │ bandwidth.bytes_per_second  vs base               │
IperfOneConnection/operation.Upload-20                                 4.537G ±  8%                 4.537G ±  0%       ~ (p=0.956 n=16)
IperfOneConnection/operation.Download-20                               2.284G ± 98%                 2.670G ± 69%       ~ (p=0.809 n=16)
IperfManyConnections/operation.Upload/parallel.16-20                   788.9M ±  2%                 789.1M ±  0%       ~ (p=0.897 n=16)
IperfManyConnections/operation.Download/parallel.16-20                 652.2M ± 19%                 652.3M ± 19%       ~ (p=0.564 n=16)
geomean                                                                1.520G                       1.580G        +4.00%

                                                              │         before          │                     after                      │
                                                              │ requests_per_second.QPS │ requests_per_second.QPS  vs base               │
NginxDocSize/filesize.1Kb/concurrency.100/filesystem.tmpfs-20               40.83k ± 2%               41.67k ± 2%  +2.04% (p=0.021 n=16)
NginxDocSize/filesize.1Kb/concurrency.1/filesystem.tmpfs-20                 8.298k ± 4%               8.460k ± 4%       ~ (p=0.539 n=16)
Ruby/concurrency.25-20                                                      6.290k ± 2%               6.256k ± 2%       ~ (p=0.918 n=16)
geomean                                                                     12.87k                    13.02k       +1.15%
```

All other benchmarks show no changes after n=16.
2026-07-10 21:09:44 -07:00

56 lines
2.0 KiB
Go

// Copyright 2026 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package systrap
import (
"testing"
"unsafe"
"gvisor.dev/gvisor/pkg/hostarch"
)
// TestContextQueueLayout ensures layout consistency of contextQueue.
func TestContextQueueLayout(t *testing.T) {
var q contextQueue
const line = uintptr(hostarch.CacheLineSize)
for _, tc := range []struct {
field string
got uintptr
want uintptr
}{
{"end", unsafe.Offsetof(q.end), 0 * line},
{"start", unsafe.Offsetof(q.start), 1 * line},
{"fastPathDisabled", unsafe.Offsetof(q.fastPathDisabled), 2 * line},
{"numAwakeContexts", unsafe.Offsetof(q.numAwakeContexts), 2*line + 4},
{"numActiveThreads", unsafe.Offsetof(q.numActiveThreads), 3 * line},
{"numSpinningThreads", unsafe.Offsetof(q.numSpinningThreads), 3*line + 4},
{"numThreadsToWakeup", unsafe.Offsetof(q.numThreadsToWakeup), 4 * line},
{"numActiveContexts", unsafe.Offsetof(q.numActiveContexts), 5 * line},
{"usedFastPath", unsafe.Offsetof(q.usedFastPath), 5*line + 4},
{"ringbuffer", unsafe.Offsetof(q.ringbuffer), 6 * line},
} {
if tc.got != tc.want {
t.Errorf("unsafe.Offsetof(contextQueue.%s) = %d, want %d", tc.field, tc.got, tc.want)
}
}
if got, want := unsafe.Sizeof(q), 6*line+uintptr(maxContextQueueEntries)*8; got != want {
t.Errorf("unsafe.Sizeof(contextQueue{}) = %d, want %d", got, want)
}
// Go's sync/atomic requires 64-bit words to be 8-byte aligned.
if off := unsafe.Offsetof(q.ringbuffer); off%8 != 0 {
t.Errorf("ringbuffer offset %d is not 8-byte aligned", off)
}
}