Skip to content

Commit 241f006

Browse files
committed
[test] Fix ~10% false pass rate without local hash seeds
1 parent acfc1b9 commit 241f006

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

validation-test/stdlib/Dictionary.swift

+23-12
Original file line numberDiff line numberDiff line change
@@ -4575,34 +4575,45 @@ DictionaryTestSuite.test("removeAt") {
45754575
}
45764576

45774577
DictionaryTestSuite.test("localHashSeeds") {
4578-
// With global hashing, copying elements in hash order between dictionaries
4578+
// With global hashing, copying elements in hash order between hash tables
45794579
// can become quadratic. (See https://bugs.swift.org/browse/SR-3268)
45804580
//
45814581
// We defeat this by mixing the local storage capacity into the global hash
45824582
// seed, thereby breaking the correlation between bucket indices across
4583-
// dictionaries with different sizes.
4583+
// hash tables with different sizes.
45844584
//
4585-
// Verify this works by copying the 1% of elements near the beginning of a
4586-
// large Dictionary into a smaller one. If the elements end up in the same
4587-
// order in the smaller Dictionary, then that indicates we do not use
4588-
// size-dependent seeding.
4585+
// Verify this works by copying a small sampling of elements near the
4586+
// beginning of a large Dictionary into a smaller one. If the elements end up
4587+
// in the same order in the smaller Dictionary, then that indicates we do not
4588+
// use size-dependent seeding.
4589+
45894590
let count = 100_000
4590-
var large = [Int: Int](minimumCapacity: count)
4591+
// Set a large table size to reduce frequency/length of collision chains.
4592+
var large = [Int: Int](minimumCapacity: 4 * count)
45914593
for i in 1 ..< count {
45924594
large[i] = 2 * i
45934595
}
45944596

4595-
// Take the second 1% of elements. The hash table may begin with collided
4596-
// elements wrapped over from the end -- we need to skip over these, as they
4597-
// would be sorted into irregular slots in the smaller table.
4598-
let slice = large.prefix(2 * count / 100).dropFirst(count / 100)
4597+
let bunch = count / 100 // 1 percent's worth of elements
4598+
4599+
// Copy two bunches of elements into another dictionary that's half the size
4600+
// of the first. We start after the initial bunch because the hash table may
4601+
// begin with collided elements wrapped over from the end, and these would be
4602+
// sorted into irregular slots in the smaller table.
4603+
let slice = large.prefix(3 * bunch).dropFirst(bunch)
45994604
var small = [Int: Int](minimumCapacity: large.capacity / 2)
46004605
expectLT(small.capacity, large.capacity)
46014606
for (key, value) in slice {
46024607
small[key] = value
46034608
}
4609+
4610+
// Compare the second halves of the new dictionary and the slice. Ignore the
4611+
// first halves; the first few elements may not be in the correct order if we
4612+
// happened to start copying from the middle of a collision chain.
4613+
let smallKeys = small.dropFirst(bunch).map { $0.key }
4614+
let sliceKeys = slice.dropFirst(bunch).map { $0.key }
46044615
// If this test fails, there is a problem with local hash seeding.
4605-
expectFalse(small.map{$0.key}.elementsEqual(slice.map{$0.key}))
4616+
expectFalse(smallKeys.elementsEqual(sliceKeys))
46064617
}
46074618

46084619
DictionaryTestSuite.setUp {

validation-test/stdlib/Set.swift

+27-15
Original file line numberDiff line numberDiff line change
@@ -4259,33 +4259,45 @@ SetTestSuite.test("SetAlgebra.UpdateWith.EmptySet") {
42594259
}
42604260

42614261
SetTestSuite.test("localHashSeeds") {
4262-
// With global hashing, copying elements in hash order between sets can become
4263-
// quadratic. (See https://bugs.swift.org/browse/SR-3268)
4262+
// With global hashing, copying elements in hash order between hash tables
4263+
// can become quadratic. (See https://bugs.swift.org/browse/SR-3268)
42644264
//
42654265
// We defeat this by mixing the local storage capacity into the global hash
42664266
// seed, thereby breaking the correlation between bucket indices across
4267-
// dictionaries with different sizes.
4267+
// hash tables with different sizes.
42684268
//
4269-
// Verify this works by copying the 1% of elements near the beginning of a
4270-
// large Set into a smaller one. If the elements end up in the same order in
4271-
// the smaller Set, then that indicates we do not use size-dependent seeding.
4269+
// Verify this works by copying a small sampling of elements near the
4270+
// beginning of a large Set into a smaller one. If the elements end up in the
4271+
// same order in the smaller Set, then that indicates we do not use
4272+
// size-dependent seeding.
4273+
42724274
let count = 100_000
4273-
var large = Set<Int>(minimumCapacity: count)
4275+
// Set a large table size to reduce frequency/length of collision chains.
4276+
var large = Set<Int>(minimumCapacity: 4 * count)
42744277
for i in 1 ..< count {
42754278
large.insert(i)
42764279
}
42774280

4278-
// Take the second 1% of elements. The hash table may begin with collided
4279-
// elements wrapped over from the end -- we need to skip over these, as they
4280-
// would be sorted into irregular slots in the smaller table.
4281-
let slice = large.prefix(2 * count / 100).dropFirst(count / 100)
4281+
let bunch = count / 100 // 1 percent's worth of elements
4282+
4283+
// Copy two bunches of elements into another set that's half the size of the
4284+
// first. We start after the initial bunch because the hash table may begin
4285+
// with collided elements wrapped over from the end, and these would be sorted
4286+
// into irregular slots in the smaller table.
4287+
let slice = large.prefix(3 * bunch).dropFirst(bunch)
42824288
var small = Set<Int>(minimumCapacity: large.capacity / 2)
42834289
expectLT(small.capacity, large.capacity)
4284-
for key in slice {
4285-
small.insert(key)
4290+
for element in slice {
4291+
small.insert(element)
42864292
}
4287-
// If this test fails, there a problem with local hash seeding.
4288-
expectFalse(small.elementsEqual(slice))
4293+
4294+
// Compare the second halves of the new set and the slice. Ignore the first
4295+
// halves; the first few elements may not be in the correct order if we
4296+
// happened to start copying from the middle of a collision chain.
4297+
let smallElements = small.dropFirst(bunch)
4298+
let sliceElements = slice.dropFirst(bunch)
4299+
// If this test fails, there is a problem with local hash seeding.
4300+
expectFalse(smallElements.elementsEqual(sliceElements))
42894301
}
42904302

42914303
runAllTests()

0 commit comments

Comments
 (0)