From 241f006ddcae991d396bfe99ecaee53910d80098 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 15 Mar 2018 22:22:01 +0000 Subject: [PATCH] [test] Fix ~10% false pass rate without local hash seeds --- validation-test/stdlib/Dictionary.swift | 35 ++++++++++++++------- validation-test/stdlib/Set.swift | 42 ++++++++++++++++--------- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/validation-test/stdlib/Dictionary.swift b/validation-test/stdlib/Dictionary.swift index 48a79aa6dc0..4061f913a2d 100644 --- a/validation-test/stdlib/Dictionary.swift +++ b/validation-test/stdlib/Dictionary.swift @@ -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 { diff --git a/validation-test/stdlib/Set.swift b/validation-test/stdlib/Set.swift index 7785d46baee..93c1a56272a 100644 --- a/validation-test/stdlib/Set.swift +++ b/validation-test/stdlib/Set.swift @@ -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(minimumCapacity: count) + // Set a large table size to reduce frequency/length of collision chains. + var large = Set(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(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()