已合并
runtime: add ClearSpan experiment to clear spans on first allocation #61
runtime: add ClearSpan experiment to clear spans on first allocation #61
已合并
cyf_123123创建于 5月13日
共 6 个文件变更+88-2
@@ -0,0 +1,8 @@
1+// Code generated by mkconsts.go. DO NOT EDIT.
2+ 
3+//go:build !goexperiment.clearspan
4+ 
5+package goexperiment
6+ 
7+const ClearSpan = false
8+const ClearSpanInt = 0
@@ -0,0 +1,8 @@
1+// Code generated by mkconsts.go. DO NOT EDIT.
2+ 
3+//go:build goexperiment.clearspan
4+ 
5+package goexperiment
6+ 
7+const ClearSpan = true
8+const ClearSpanInt = 1
@@ -137,4 +137,7 @@ type Flags struct {
137 137 
138 // RevertCopyHashKeys enables the "revert" hash map implementation.138 // RevertCopyHashKeys enables the "revert" hash map implementation.
139 RevertCopyHashKeys bool139 RevertCopyHashKeys bool
140+
141+ // Clear the span if necessary
142+ ClearSpan bool
140}143}
@@ -0,0 +1,19 @@
1+// Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
2+package runtime_test
3+ 
4+import (
5+ "internal/goexperiment"
6+ "runtime"
7+ "testing"
8+)
9+ 
10+func TestClearSpan(t *testing.T) {
11+ needzero, cleared := runtime.TryClearSpanFunc()
12+ if goexperiment.ClearSpan {
13+ if needzero != 0 || !cleared {
14+ t.Fatalf("ClearSpan enabled: expected needzero=0 and cleared=true, got needzero=%d cleared=%v", needzero, cleared)
15+ }
16+ } else if needzero == 0 || cleared {
17+ t.Fatalf("ClearSpan disabled: expected needzero!=0 and cleared=false, got needzero=%d cleared=%v", needzero, cleared)
18+ }
19+}
@@ -0,0 +1,35 @@
1+// Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
2+ 
3+package runtime
4+ 
5+import "unsafe"
6+ 
7+func TryClearSpanFunc() (needzero uint8, cleared bool) {
8+ buf := make([]byte, 128)
9+ for i := range buf {
10+ buf[i] = 0xff
11+ }
12+ 
13+ ms := AllocMSpan()
14+ defer FreeMSpan(ms)
15+ 
16+ s := (*mspan)(unsafe.Pointer(ms))
17+ s.startAddr = uintptr(unsafe.Pointer(&buf[0]))
18+ s.elemsize = 16
19+ s.nelems = uint16(len(buf) / int(s.elemsize))
20+ s.allocCount = 0
21+ s.needzero = 1
22+ 
23+ clearSpanFunc(s)
24+ 
25+ needzero = s.needzero
26+ cleared = true
27+ for _, b := range buf {
28+ if b != 0 {
29+ cleared = false
30+ break
31+ }
32+ }
33+ KeepAlive(buf)
34+ return
atomgit-bot
atomgit-botatomgit-bot5月14日

🟡 Medium Priority

TryClearSpanFunc 通过 allocmcache() 获取 mcache,其 alloc[spc] 指向全局 emptymspan(见 mcache.go:95 初始化逻辑)。测试直接修改 emptymspan.needzero 并对其调用 clearSpanFunc,这会触发 memclrNoHeapPointers(unsafe.Pointer(0), 0)。虽然各平台 memclrNoHeapPointers 实现对 size=0 均安全返回,但修改全局 emptymspan 破坏了测试隔离性,并行测试下可能触发 race detector 报警,且 emptymspan.needzero 被永久修改可能影响后续依赖该值的逻辑。

同一段代码其他问题

  • 导出的变量 Class_to_size / Size_to_class8 / Size_to_class128 未被使用:这三个变量在该文件及整个 diff 范围内均未被任何测试引用,属于无用的导出,增加维护负担并可被误认为有外部依赖。
likedislike
不准确?
cyf_123123
cyf_123123
5月18日 评论:
35+}
@@ -102,6 +102,7 @@ package runtime
102 102 
103import (103import (
104 "internal/goarch"104 "internal/goarch"
105+ "internal/goexperiment"
105 "internal/goos"106 "internal/goos"
106 "internal/runtime/atomic"107 "internal/runtime/atomic"
107 "internal/runtime/math"108 "internal/runtime/math"
@@ -972,6 +973,9 @@ func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, checkGCTrigger
972 throw("freeIndex is not valid")973 throw("freeIndex is not valid")
973 }974 }
974 975 
976+ // Clear the span if necessary
977+ clearSpanFunc(s)
978+ 
975 v = gclinkptr(uintptr(freeIndex)*s.elemsize + s.base())979 v = gclinkptr(uintptr(freeIndex)*s.elemsize + s.base())
976 s.allocCount++980 s.allocCount++
977 if s.allocCount > s.nelems {981 if s.allocCount > s.nelems {
@@ -981,6 +985,13 @@ func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, checkGCTrigger
981 return985 return
982}986}
983 987 
988+func clearSpanFunc(s *mspan) {
989+ if goexperiment.ClearSpan && (s.allocCount == 0) && (s.needzero != 0) {
990+ memclrNoHeapPointers(unsafe.Pointer(s.startAddr), (s.elemsize * uintptr(s.nelems)))
991+ s.needzero = 0
992+ }
993+}
994+ 
984// doubleCheckMalloc enables a bunch of extra checks to malloc to double-check995// doubleCheckMalloc enables a bunch of extra checks to malloc to double-check
985// that various invariants are upheld.996// that various invariants are upheld.
986//997//
@@ -1175,8 +1186,10 @@ func mallocgcTiny(size uintptr, typ *_type, needzero bool) (unsafe.Pointer, uint
1175 v, span, checkGCTrigger = c.nextFree(tinySpanClass)1186 v, span, checkGCTrigger = c.nextFree(tinySpanClass)
1176 }1187 }
1177 x := unsafe.Pointer(v)1188 x := unsafe.Pointer(v)
1178- (*[2]uint64)(x)[0] = 01189+ if !goexperiment.ClearSpan || span.needzero != 0 {
1179- (*[2]uint64)(x)[1] = 01190+ (*[2]uint64)(x)[0] = 0
1191+ (*[2]uint64)(x)[1] = 0
1192+ }
1180 // See if we need to replace the existing tiny block with the new one1193 // See if we need to replace the existing tiny block with the new one
1181 // based on amount of remaining free space.1194 // based on amount of remaining free space.
1182 if !raceenabled && (size < c.tinyoffset || c.tiny == 0) {1195 if !raceenabled && (size < c.tinyoffset || c.tiny == 0) {