github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/runtime/mpagecache_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime_test 6 7 import ( 8 "github.com/x04/go/src/math/rand" 9 . "github.com/x04/go/src/runtime" 10 "github.com/x04/go/src/testing" 11 ) 12 13 func checkPageCache(t *testing.T, got, want PageCache) { 14 if got.Base() != want.Base() { 15 t.Errorf("bad pageCache base: got 0x%x, want 0x%x", got.Base(), want.Base()) 16 } 17 if got.Cache() != want.Cache() { 18 t.Errorf("bad pageCache bits: got %016x, want %016x", got.Base(), want.Base()) 19 } 20 if got.Scav() != want.Scav() { 21 t.Errorf("bad pageCache scav: got %016x, want %016x", got.Scav(), want.Scav()) 22 } 23 } 24 25 func TestPageCacheAlloc(t *testing.T) { 26 base := PageBase(BaseChunkIdx, 0) 27 type hit struct { 28 npages uintptr 29 base uintptr 30 scav uintptr 31 } 32 tests := map[string]struct { 33 cache PageCache 34 hits []hit 35 }{ 36 "Empty": { 37 cache: NewPageCache(base, 0, 0), 38 hits: []hit{ 39 {1, 0, 0}, 40 {2, 0, 0}, 41 {3, 0, 0}, 42 {4, 0, 0}, 43 {5, 0, 0}, 44 {11, 0, 0}, 45 {12, 0, 0}, 46 {16, 0, 0}, 47 {27, 0, 0}, 48 {32, 0, 0}, 49 {43, 0, 0}, 50 {57, 0, 0}, 51 {64, 0, 0}, 52 {121, 0, 0}, 53 }, 54 }, 55 "Lo1": { 56 cache: NewPageCache(base, 0x1, 0x1), 57 hits: []hit{ 58 {1, base, PageSize}, 59 {1, 0, 0}, 60 {10, 0, 0}, 61 }, 62 }, 63 "Hi1": { 64 cache: NewPageCache(base, 0x1<<63, 0x1), 65 hits: []hit{ 66 {1, base + 63*PageSize, 0}, 67 {1, 0, 0}, 68 {10, 0, 0}, 69 }, 70 }, 71 "Swiss1": { 72 cache: NewPageCache(base, 0x20005555, 0x5505), 73 hits: []hit{ 74 {2, 0, 0}, 75 {1, base, PageSize}, 76 {1, base + 2*PageSize, PageSize}, 77 {1, base + 4*PageSize, 0}, 78 {1, base + 6*PageSize, 0}, 79 {1, base + 8*PageSize, PageSize}, 80 {1, base + 10*PageSize, PageSize}, 81 {1, base + 12*PageSize, PageSize}, 82 {1, base + 14*PageSize, PageSize}, 83 {1, base + 29*PageSize, 0}, 84 {1, 0, 0}, 85 {10, 0, 0}, 86 }, 87 }, 88 "Lo2": { 89 cache: NewPageCache(base, 0x3, 0x2<<62), 90 hits: []hit{ 91 {2, base, 0}, 92 {2, 0, 0}, 93 {1, 0, 0}, 94 }, 95 }, 96 "Hi2": { 97 cache: NewPageCache(base, 0x3<<62, 0x3<<62), 98 hits: []hit{ 99 {2, base + 62*PageSize, 2 * PageSize}, 100 {2, 0, 0}, 101 {1, 0, 0}, 102 }, 103 }, 104 "Swiss2": { 105 cache: NewPageCache(base, 0x3333<<31, 0x3030<<31), 106 hits: []hit{ 107 {2, base + 31*PageSize, 0}, 108 {2, base + 35*PageSize, 2 * PageSize}, 109 {2, base + 39*PageSize, 0}, 110 {2, base + 43*PageSize, 2 * PageSize}, 111 {2, 0, 0}, 112 }, 113 }, 114 "Hi53": { 115 cache: NewPageCache(base, ((uint64(1)<<53)-1)<<10, ((uint64(1)<<16)-1)<<10), 116 hits: []hit{ 117 {53, base + 10*PageSize, 16 * PageSize}, 118 {53, 0, 0}, 119 {1, 0, 0}, 120 }, 121 }, 122 "Full53": { 123 cache: NewPageCache(base, ^uint64(0), ((uint64(1)<<16)-1)<<10), 124 hits: []hit{ 125 {53, base, 16 * PageSize}, 126 {53, 0, 0}, 127 {1, base + 53*PageSize, 0}, 128 }, 129 }, 130 "Full64": { 131 cache: NewPageCache(base, ^uint64(0), ^uint64(0)), 132 hits: []hit{ 133 {64, base, 64 * PageSize}, 134 {64, 0, 0}, 135 {1, 0, 0}, 136 }, 137 }, 138 "FullMixed": { 139 cache: NewPageCache(base, ^uint64(0), ^uint64(0)), 140 hits: []hit{ 141 {5, base, 5 * PageSize}, 142 {7, base + 5*PageSize, 7 * PageSize}, 143 {1, base + 12*PageSize, 1 * PageSize}, 144 {23, base + 13*PageSize, 23 * PageSize}, 145 {63, 0, 0}, 146 {3, base + 36*PageSize, 3 * PageSize}, 147 {3, base + 39*PageSize, 3 * PageSize}, 148 {3, base + 42*PageSize, 3 * PageSize}, 149 {12, base + 45*PageSize, 12 * PageSize}, 150 {11, 0, 0}, 151 {4, base + 57*PageSize, 4 * PageSize}, 152 {4, 0, 0}, 153 {6, 0, 0}, 154 {36, 0, 0}, 155 {2, base + 61*PageSize, 2 * PageSize}, 156 {3, 0, 0}, 157 {1, base + 63*PageSize, 1 * PageSize}, 158 {4, 0, 0}, 159 {2, 0, 0}, 160 {62, 0, 0}, 161 {1, 0, 0}, 162 }, 163 }, 164 } 165 for name, test := range tests { 166 test := test 167 t.Run(name, func(t *testing.T) { 168 c := test.cache 169 for i, h := range test.hits { 170 b, s := c.Alloc(h.npages) 171 if b != h.base { 172 t.Fatalf("bad alloc base #%d: got 0x%x, want 0x%x", i, b, h.base) 173 } 174 if s != h.scav { 175 t.Fatalf("bad alloc scav #%d: got %d, want %d", i, s, h.scav) 176 } 177 } 178 }) 179 } 180 } 181 182 func TestPageCacheFlush(t *testing.T) { 183 if GOOS == "openbsd" && testing.Short() { 184 t.Skip("skipping because virtual memory is limited; see #36210") 185 } 186 bits64ToBitRanges := func(bits uint64, base uint) []BitRange { 187 var ranges []BitRange 188 start, size := uint(0), uint(0) 189 for i := 0; i < 64; i++ { 190 if bits&(1<<i) != 0 { 191 if size == 0 { 192 start = uint(i) + base 193 } 194 size++ 195 } else { 196 if size != 0 { 197 ranges = append(ranges, BitRange{start, size}) 198 size = 0 199 } 200 } 201 } 202 if size != 0 { 203 ranges = append(ranges, BitRange{start, size}) 204 } 205 return ranges 206 } 207 runTest := func(t *testing.T, base uint, cache, scav uint64) { 208 // Set up the before state. 209 beforeAlloc := map[ChunkIdx][]BitRange{ 210 BaseChunkIdx: {{base, 64}}, 211 } 212 beforeScav := map[ChunkIdx][]BitRange{ 213 BaseChunkIdx: {}, 214 } 215 b := NewPageAlloc(beforeAlloc, beforeScav) 216 defer FreePageAlloc(b) 217 218 // Create and flush the cache. 219 c := NewPageCache(PageBase(BaseChunkIdx, base), cache, scav) 220 c.Flush(b) 221 if !c.Empty() { 222 t.Errorf("pageCache flush did not clear cache") 223 } 224 225 // Set up the expected after state. 226 afterAlloc := map[ChunkIdx][]BitRange{ 227 BaseChunkIdx: bits64ToBitRanges(^cache, base), 228 } 229 afterScav := map[ChunkIdx][]BitRange{ 230 BaseChunkIdx: bits64ToBitRanges(scav, base), 231 } 232 want := NewPageAlloc(afterAlloc, afterScav) 233 defer FreePageAlloc(want) 234 235 // Check to see if it worked. 236 checkPageAlloc(t, want, b) 237 } 238 239 // Empty. 240 runTest(t, 0, 0, 0) 241 242 // Full. 243 runTest(t, 0, ^uint64(0), ^uint64(0)) 244 245 // Random. 246 for i := 0; i < 100; i++ { 247 // Generate random valid base within a chunk. 248 base := uint(rand.Intn(PallocChunkPages/64)) * 64 249 250 // Generate random cache. 251 cache := rand.Uint64() 252 scav := rand.Uint64() & cache 253 254 // Run the test. 255 runTest(t, base, cache, scav) 256 } 257 } 258 259 func TestPageAllocAllocToCache(t *testing.T) { 260 if GOOS == "openbsd" && testing.Short() { 261 t.Skip("skipping because virtual memory is limited; see #36210") 262 } 263 tests := map[string]struct { 264 before map[ChunkIdx][]BitRange 265 scav map[ChunkIdx][]BitRange 266 hits []PageCache // expected base addresses and patterns 267 after map[ChunkIdx][]BitRange 268 }{ 269 "AllFree": { 270 before: map[ChunkIdx][]BitRange{ 271 BaseChunkIdx: {}, 272 }, 273 scav: map[ChunkIdx][]BitRange{ 274 BaseChunkIdx: {{1, 1}, {64, 64}}, 275 }, 276 hits: []PageCache{ 277 NewPageCache(PageBase(BaseChunkIdx, 0), ^uint64(0), 0x2), 278 NewPageCache(PageBase(BaseChunkIdx, 64), ^uint64(0), ^uint64(0)), 279 NewPageCache(PageBase(BaseChunkIdx, 128), ^uint64(0), 0), 280 NewPageCache(PageBase(BaseChunkIdx, 192), ^uint64(0), 0), 281 }, 282 after: map[ChunkIdx][]BitRange{ 283 BaseChunkIdx: {{0, 256}}, 284 }, 285 }, 286 "ManyArena": { 287 before: map[ChunkIdx][]BitRange{ 288 BaseChunkIdx: {{0, PallocChunkPages}}, 289 BaseChunkIdx + 1: {{0, PallocChunkPages}}, 290 BaseChunkIdx + 2: {{0, PallocChunkPages - 64}}, 291 }, 292 scav: map[ChunkIdx][]BitRange{ 293 BaseChunkIdx: {{0, PallocChunkPages}}, 294 BaseChunkIdx + 1: {{0, PallocChunkPages}}, 295 BaseChunkIdx + 2: {}, 296 }, 297 hits: []PageCache{ 298 NewPageCache(PageBase(BaseChunkIdx+2, PallocChunkPages-64), ^uint64(0), 0), 299 }, 300 after: map[ChunkIdx][]BitRange{ 301 BaseChunkIdx: {{0, PallocChunkPages}}, 302 BaseChunkIdx + 1: {{0, PallocChunkPages}}, 303 BaseChunkIdx + 2: {{0, PallocChunkPages}}, 304 }, 305 }, 306 "NotContiguous": { 307 before: map[ChunkIdx][]BitRange{ 308 BaseChunkIdx: {{0, PallocChunkPages}}, 309 BaseChunkIdx + 0xff: {{0, 0}}, 310 }, 311 scav: map[ChunkIdx][]BitRange{ 312 BaseChunkIdx: {{0, PallocChunkPages}}, 313 BaseChunkIdx + 0xff: {{31, 67}}, 314 }, 315 hits: []PageCache{ 316 NewPageCache(PageBase(BaseChunkIdx+0xff, 0), ^uint64(0), ((uint64(1)<<33)-1)<<31), 317 }, 318 after: map[ChunkIdx][]BitRange{ 319 BaseChunkIdx: {{0, PallocChunkPages}}, 320 BaseChunkIdx + 0xff: {{0, 64}}, 321 }, 322 }, 323 "First": { 324 before: map[ChunkIdx][]BitRange{ 325 BaseChunkIdx: {{0, 32}, {33, 31}, {96, 32}}, 326 }, 327 scav: map[ChunkIdx][]BitRange{ 328 BaseChunkIdx: {{1, 4}, {31, 5}, {66, 2}}, 329 }, 330 hits: []PageCache{ 331 NewPageCache(PageBase(BaseChunkIdx, 0), 1<<32, 1<<32), 332 NewPageCache(PageBase(BaseChunkIdx, 64), (uint64(1)<<32)-1, 0x3<<2), 333 }, 334 after: map[ChunkIdx][]BitRange{ 335 BaseChunkIdx: {{0, 128}}, 336 }, 337 }, 338 "Fail": { 339 before: map[ChunkIdx][]BitRange{ 340 BaseChunkIdx: {{0, PallocChunkPages}}, 341 }, 342 hits: []PageCache{ 343 NewPageCache(0, 0, 0), 344 NewPageCache(0, 0, 0), 345 NewPageCache(0, 0, 0), 346 }, 347 after: map[ChunkIdx][]BitRange{ 348 BaseChunkIdx: {{0, PallocChunkPages}}, 349 }, 350 }, 351 } 352 for name, v := range tests { 353 v := v 354 t.Run(name, func(t *testing.T) { 355 b := NewPageAlloc(v.before, v.scav) 356 defer FreePageAlloc(b) 357 358 for _, expect := range v.hits { 359 checkPageCache(t, b.AllocToCache(), expect) 360 if t.Failed() { 361 return 362 } 363 } 364 want := NewPageAlloc(v.after, v.scav) 365 defer FreePageAlloc(want) 366 367 checkPageAlloc(t, want, b) 368 }) 369 } 370 }