[test] Fix ~10% false pass rate without local hash seeds

This commit is contained in:
Karoy Lorentey
2018-03-15 22:22:01 +00:00
parent acfc1b90d4
commit 241f006ddc
2 changed files with 50 additions and 27 deletions
+23 -12
View File
@@ -4575,34 +4575,45 @@ DictionaryTestSuite.test("removeAt") {
}
DictionaryTestSuite.test("localHashSeeds") {
// With global hashing, copying elements in hash order between dictionaries
// With global hashing, copying elements in hash order between hash tables
// can become quadratic. (See https://bugs.swift.org/browse/SR-3268)
//
// We defeat this by mixing the local storage capacity into the global hash
// seed, thereby breaking the correlation between bucket indices across
// dictionaries with different sizes.
// hash tables with different sizes.
//
// Verify this works by copying the 1% of elements near the beginning of a
// large Dictionary into a smaller one. If the elements end up in the same
// order in the smaller Dictionary, then that indicates we do not use
// size-dependent seeding.
// Verify this works by copying a small sampling of elements near the
// beginning of a large Dictionary into a smaller one. If the elements end up
// in the same order in the smaller Dictionary, then that indicates we do not
// use size-dependent seeding.
let count = 100_000
var large = [Int: Int](minimumCapacity: count)
// Set a large table size to reduce frequency/length of collision chains.
var large = [Int: Int](minimumCapacity: 4 * count)
for i in 1 ..< count {
large[i] = 2 * i
}
// Take the second 1% of elements. The hash table may begin with collided
// elements wrapped over from the end -- we need to skip over these, as they
// would be sorted into irregular slots in the smaller table.
let slice = large.prefix(2 * count / 100).dropFirst(count / 100)
let bunch = count / 100 // 1 percent's worth of elements
// Copy two bunches of elements into another dictionary that's half the size
// of the first. We start after the initial bunch because the hash table may
// begin with collided elements wrapped over from the end, and these would be
// sorted into irregular slots in the smaller table.
let slice = large.prefix(3 * bunch).dropFirst(bunch)
var small = [Int: Int](minimumCapacity: large.capacity / 2)
expectLT(small.capacity, large.capacity)
for (key, value) in slice {
small[key] = value
}
// Compare the second halves of the new dictionary and the slice. Ignore the
// first halves; the first few elements may not be in the correct order if we
// happened to start copying from the middle of a collision chain.
let smallKeys = small.dropFirst(bunch).map { $0.key }
let sliceKeys = slice.dropFirst(bunch).map { $0.key }
// If this test fails, there is a problem with local hash seeding.
expectFalse(small.map{$0.key}.elementsEqual(slice.map{$0.key}))
expectFalse(smallKeys.elementsEqual(sliceKeys))
}
DictionaryTestSuite.setUp {
+27 -15
View File
@@ -4259,33 +4259,45 @@ SetTestSuite.test("SetAlgebra.UpdateWith.EmptySet") {
}
SetTestSuite.test("localHashSeeds") {
// With global hashing, copying elements in hash order between sets can become
// quadratic. (See https://bugs.swift.org/browse/SR-3268)
// With global hashing, copying elements in hash order between hash tables
// can become quadratic. (See https://bugs.swift.org/browse/SR-3268)
//
// We defeat this by mixing the local storage capacity into the global hash
// seed, thereby breaking the correlation between bucket indices across
// dictionaries with different sizes.
// hash tables with different sizes.
//
// Verify this works by copying the 1% of elements near the beginning of a
// large Set into a smaller one. If the elements end up in the same order in
// the smaller Set, then that indicates we do not use size-dependent seeding.
// Verify this works by copying a small sampling of elements near the
// beginning of a large Set into a smaller one. If the elements end up in the
// same order in the smaller Set, then that indicates we do not use
// size-dependent seeding.
let count = 100_000
var large = Set<Int>(minimumCapacity: count)
// Set a large table size to reduce frequency/length of collision chains.
var large = Set<Int>(minimumCapacity: 4 * count)
for i in 1 ..< count {
large.insert(i)
}
// Take the second 1% of elements. The hash table may begin with collided
// elements wrapped over from the end -- we need to skip over these, as they
// would be sorted into irregular slots in the smaller table.
let slice = large.prefix(2 * count / 100).dropFirst(count / 100)
let bunch = count / 100 // 1 percent's worth of elements
// Copy two bunches of elements into another set that's half the size of the
// first. We start after the initial bunch because the hash table may begin
// with collided elements wrapped over from the end, and these would be sorted
// into irregular slots in the smaller table.
let slice = large.prefix(3 * bunch).dropFirst(bunch)
var small = Set<Int>(minimumCapacity: large.capacity / 2)
expectLT(small.capacity, large.capacity)
for key in slice {
small.insert(key)
for element in slice {
small.insert(element)
}
// If this test fails, there a problem with local hash seeding.
expectFalse(small.elementsEqual(slice))
// Compare the second halves of the new set and the slice. Ignore the first
// halves; the first few elements may not be in the correct order if we
// happened to start copying from the middle of a collision chain.
let smallElements = small.dropFirst(bunch)
let sliceElements = slice.dropFirst(bunch)
// If this test fails, there is a problem with local hash seeding.
expectFalse(smallElements.elementsEqual(sliceElements))
}
runAllTests()