mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
stdlib: add a function to squeeze a number in a given range from a hash value
This function mixes the bits in the hash value, which improves Dictionary performance for keys with bad hashes. PrecommitBenchmark changes with greater than 7% difference: ``````````Dictionary2`,```1456.00`,```1508.00`,```1502.00`,````624.00`,````607.00`,````592.00`,`864.00`,``145.9% ``````````Dictionary3`,```1379.00`,```1439.00`,```1408.00`,````585.00`,````567.00`,````552.00`,`827.00`,``149.8% ````````````Histogram`,````850.00`,````849.00`,````851.00`,```1053.00`,```1049.00`,```1048.00`,`199.00`,``-19.0% ````````````````Prims`,```1999.00`,```2005.00`,```2018.00`,```1734.00`,```1689.00`,```1701.00`,`310.00`,```18.4% ``````````StrSplitter`,```2365.00`,```2334.00`,```2316.00`,```1979.00`,```1997.00`,```2000.00`,`337.00`,```17.0% ```````````````TwoSum`,```1551.00`,```1568.00`,```1556.00`,```1771.00`,```1741.00`,```1716.00`,`165.00`,```-9.6% Regressions are in benchmarks that use `Int` as dictionary key: we are just doing more work than previously (hashing an `Int` was an identity function). rdar://17962402 Swift SVN r21142
This commit is contained in:
@@ -45,13 +45,6 @@ func strideofValue<T>(_:T) -> Int {
|
||||
return strideof(T.self)
|
||||
}
|
||||
|
||||
/// Returns if x is a power of 2, assuming that x is not 0
|
||||
@transparent
|
||||
func _isPowerOf2(v : Int) -> Bool {
|
||||
_sanityCheck(v > 0)
|
||||
return (v & (v - 1)) == 0
|
||||
}
|
||||
|
||||
func _roundUpToAlignment(offset: Int, alignment: Int) -> Int {
|
||||
_sanityCheck(offset >= 0)
|
||||
_sanityCheck(alignment > 0)
|
||||
|
||||
@@ -46,6 +46,7 @@ set(SWIFTLIB_ESSENTIAL
|
||||
FixedPoint.swift.gyb
|
||||
FloatingPoint.swift.gyb
|
||||
FloatingPointOperations.swift.gyb
|
||||
Hashing.swift
|
||||
HeapBuffer.swift
|
||||
ImplicitlyUnwrappedOptional.swift
|
||||
Index.swift
|
||||
|
||||
@@ -380,7 +380,7 @@ struct _NativeDictionaryStorage<Key : Hashable, Value> :
|
||||
}
|
||||
|
||||
func _bucket(k: Key) -> Int {
|
||||
return k.hashValue & _bucketMask
|
||||
return _squeezeHashValue(k.hashValue, 0..<capacity)
|
||||
}
|
||||
|
||||
func _next(bucket: Int) -> Int {
|
||||
|
||||
152
stdlib/core/Hashing.swift
Normal file
152
stdlib/core/Hashing.swift
Normal file
@@ -0,0 +1,152 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See http://swift.org/LICENSE.txt for license information
|
||||
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//
|
||||
// This file implements helpers for constructing non-cryptographic hash
|
||||
// functions.
|
||||
//
|
||||
// This code was ported from LLVM's ADT/Hashing.h.
|
||||
//
|
||||
// Currently the algorithm is based on CityHash, but this is an implementation
|
||||
// detail. Even more, there are facilities to mix in a per-execution seed to
|
||||
// ensure that hash values differ between executions.
|
||||
//
|
||||
|
||||
struct _HashingDetail {
|
||||
static var fixedSeedOverride: UInt64 = 0
|
||||
|
||||
@transparent
|
||||
static func getExecutionSeed() -> UInt64 {
|
||||
// FIXME: This needs to be a per-execution seed. This is just a placeholder
|
||||
// implementation.
|
||||
let seed: UInt64 = 0xff51afd7ed558ccd
|
||||
return _HashingDetail.fixedSeedOverride == 0 ? seed : fixedSeedOverride
|
||||
}
|
||||
|
||||
@transparent
|
||||
static func hash16Bytes(low: UInt64, _ high: UInt64) -> UInt64 {
|
||||
// Murmur-inspired hashing.
|
||||
let mul: UInt64 = 0x9ddfea08eb382d69
|
||||
var a: UInt64 = (low ^ high) &* mul
|
||||
a ^= (a >> 47)
|
||||
var b: UInt64 = (high ^ a) &* mul
|
||||
b ^= (b >> 47)
|
||||
b = b &* mul
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// API functions.
|
||||
//
|
||||
|
||||
//
|
||||
// _mix*() functions all have type (T) -> T. These functions don't compress
|
||||
// their inputs and just exhibit avalance effect.
|
||||
//
|
||||
|
||||
@transparent
|
||||
func _mixUInt32(value: UInt32) -> UInt32 {
|
||||
// Zero-extend to 64 bits, hash, select 32 bits from the hash.
|
||||
//
|
||||
// NOTE: this differs from LLVM's implementation, which selects the lower
|
||||
// 32 bits. According to the statistical tests, the 3 lowest bits have
|
||||
// weaker avalanche properties.
|
||||
let extendedValue = UInt64(value)
|
||||
let extendedResult = _mixUInt64(extendedValue)
|
||||
return UInt32((extendedResult >> 3) & 0xffff_ffff)
|
||||
}
|
||||
|
||||
@transparent
|
||||
func _mixInt32(value: Int32) -> Int32 {
|
||||
return Int32(bitPattern: _mixUInt32(UInt32(bitPattern: value)))
|
||||
}
|
||||
|
||||
@transparent
|
||||
func _mixUInt64(value: UInt64) -> UInt64 {
|
||||
// Similar to hash_4to8_bytes but using a seed instead of length.
|
||||
let seed: UInt64 = _HashingDetail.getExecutionSeed()
|
||||
let low: UInt64 = value & 0xffff_ffff
|
||||
let high: UInt64 = value >> 32
|
||||
return _HashingDetail.hash16Bytes(seed + (low << 3), high);
|
||||
}
|
||||
|
||||
@transparent
|
||||
func _mixInt64(value: Int64) -> Int64 {
|
||||
return Int64(bitPattern: _mixUInt64(UInt64(bitPattern: value)))
|
||||
}
|
||||
|
||||
@transparent
|
||||
func _mixUInt(value: UInt) -> UInt {
|
||||
#if arch(i386) || arch(arm)
|
||||
return UInt(_mixUInt32(UInt32(value)))
|
||||
#elseif arch(x86_64) || arch(arm64)
|
||||
return UInt(_mixUInt64(UInt64(value)))
|
||||
#endif
|
||||
}
|
||||
|
||||
@transparent
|
||||
func _mixInt(value: Int) -> Int {
|
||||
#if arch(i386) || arch(arm)
|
||||
return Int(_mixInt32(Int32(value)))
|
||||
#elseif arch(x86_64) || arch(arm64)
|
||||
return Int(_mixInt64(Int64(value)))
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Given a hash value, returns an integer value within the given range that
|
||||
/// corresponds to a hash value.
|
||||
///
|
||||
/// This function is superior to computing the remainder of `hashValue` by
|
||||
/// the range length. Some types have bad hash functions; sometimes simple
|
||||
/// patterns in data sets create patterns in hash values and applying the
|
||||
/// remainder operation just throws away even more information and invites
|
||||
/// even more hash collisions. This effect is especially bad if the length
|
||||
/// of the required range is a power of two -- applying the remainder
|
||||
/// operation just throws away high bits of the hash (which would not be
|
||||
/// a problem if the hash was known to be good). This function mixes the
|
||||
/// bits in the hash value to compensate for such cases.
|
||||
///
|
||||
/// Of course, this function is a compressing function, and applying it to a
|
||||
/// hash value does not change anything fundamentally: collisions are still
|
||||
/// possible, and it does not prevent malicious users from constructing data
|
||||
/// sets that will exhibit pathological collisions.
|
||||
func _squeezeHashValue(hashValue: Int, resultRange: Range<Int>) -> Int {
|
||||
// Length of a Range<Int> does not fit into an Int, but fits into an UInt.
|
||||
// An efficient way to compute the length is to rely on two's complement
|
||||
// arithmetic.
|
||||
let resultCardinality =
|
||||
UInt(bitPattern: resultRange.endIndex &- resultRange.startIndex)
|
||||
|
||||
// Calculate the result as `UInt` to handle the case when
|
||||
// `resultCardinality >= Int.max`.
|
||||
let unsignedResult =
|
||||
_squeezeHashValue(hashValue, UInt(0)..<resultCardinality)
|
||||
|
||||
// We perform the unchecked arithmetic on `UInt` (instead of doing
|
||||
// straightforward computations on `Int`) in order to handle the following
|
||||
// tricky case: `startIndex` is negative, and `resultCardinality >= Int.max`.
|
||||
// We can not convert the latter to `Int`.
|
||||
return
|
||||
Int(bitPattern:
|
||||
UInt(bitPattern: resultRange.startIndex) &+ unsignedResult)
|
||||
}
|
||||
|
||||
func _squeezeHashValue(hashValue: Int, resultRange: Range<UInt>) -> UInt {
|
||||
let mixedHashValue = UInt(bitPattern: _mixInt(hashValue))
|
||||
let resultCardinality: UInt = resultRange.endIndex - resultRange.startIndex
|
||||
if _isPowerOf2(resultCardinality) {
|
||||
return mixedHashValue & (resultCardinality - 1)
|
||||
}
|
||||
return resultRange.startIndex + (mixedHashValue % resultCardinality)
|
||||
}
|
||||
|
||||
@@ -67,6 +67,28 @@ func _stdlib_atomicCompareExchangeStrongPtr<T>(
|
||||
return Int64(Builtin.int_ctlz_Int64(value.value, false.value))
|
||||
}
|
||||
|
||||
/// Returns if `x` is a power of 2.
|
||||
@transparent
|
||||
public func _isPowerOf2(x: UInt) -> Bool {
|
||||
if x == 0 {
|
||||
return false
|
||||
}
|
||||
// Note: use unchecked subtraction because we have checked that `x` is not
|
||||
// zero.
|
||||
return x & (x &- 1) == 0
|
||||
}
|
||||
|
||||
/// Returns if `x` is a power of 2.
|
||||
@transparent
|
||||
public func _isPowerOf2(x: Int) -> Bool {
|
||||
if x <= 0 {
|
||||
return false
|
||||
}
|
||||
// Note: use unchecked subtraction because we have checked that `x` is not
|
||||
// `Int.min`.
|
||||
return x & (x &- 1) == 0
|
||||
}
|
||||
|
||||
@transparent public func _autorelease(x: AnyObject) {
|
||||
Builtin.retain(x)
|
||||
Builtin.autorelease(x)
|
||||
|
||||
@@ -11,6 +11,7 @@ set(SWIFTUNITTEST_SOURCES
|
||||
OpaqueIdentityFunctions.swift
|
||||
PthreadBarriers.swift
|
||||
PthreadWrappers.swift
|
||||
Statistics.swift
|
||||
StdlibCoreExtras.swift
|
||||
SwiftBlockToCFunctionThunks.cpp
|
||||
)
|
||||
|
||||
68
stdlib/unittest/Statistics.swift
Normal file
68
stdlib/unittest/Statistics.swift
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
import Darwin
|
||||
|
||||
public func rand32() -> UInt32 {
|
||||
return arc4random()
|
||||
}
|
||||
|
||||
public func rand64() -> UInt64 {
|
||||
return (UInt64(arc4random()) << 32) | UInt64(arc4random())
|
||||
}
|
||||
|
||||
public func randInt() -> Int {
|
||||
#if arch(i386) || arch(arm)
|
||||
return Int(Int32(bitPattern: rand32()))
|
||||
#elseif arch(x86_64) || arch(arm64)
|
||||
return Int(Int64(bitPattern: rand64()))
|
||||
#else
|
||||
fatalError("unimplemented")
|
||||
#endif
|
||||
}
|
||||
|
||||
public func randArray64(count: Int) -> ContiguousArray<UInt64> {
|
||||
var result = ContiguousArray<UInt64>(count: count, repeatedValue: 0)
|
||||
for i in indices(result) {
|
||||
result[i] = rand64()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// For a given p-value, returns the critical chi-square value for
|
||||
/// a distribution with 1 degree of freedom.
|
||||
func _chiSquaredUniform1DFCritical(pValue: Double) -> Double {
|
||||
if abs(pValue - 0.05) < 0.00001 { return 0.00393214 }
|
||||
if abs(pValue - 0.02) < 0.00001 { return 0.000628450 }
|
||||
if abs(pValue - 0.01) < 0.00001 { return 0.000157088 }
|
||||
if abs(pValue - 0.007) < 0.00001 { return 0.000076971 }
|
||||
if abs(pValue - 0.005) < 0.00001 { return 0.0000392704 }
|
||||
if abs(pValue - 0.003) < 0.00001 { return 0.0000141372 }
|
||||
if abs(pValue - 0.002) < 0.00001 { return 6.2832e-6 }
|
||||
if abs(pValue - 0.001) < 0.00001 { return 1.5708e-6 }
|
||||
fatalError("unknown value")
|
||||
}
|
||||
|
||||
/// Perform chi-squared test for a discrete uniform distribution with
|
||||
/// 2 outcomes.
|
||||
public func chiSquaredUniform2(
|
||||
trials: Int, observedACount: Int, pValue: Double
|
||||
) -> Bool {
|
||||
|
||||
func square(x: Double) -> Double {
|
||||
return x * x
|
||||
}
|
||||
|
||||
let expectedA = 0.5
|
||||
let expectedB = 0.5
|
||||
let observedA = Double(observedACount) / Double(trials)
|
||||
let observedB = 1.0 - observedA
|
||||
let chiSq =
|
||||
square(observedA - expectedA) / expectedA +
|
||||
square(observedB - expectedB) / expectedB
|
||||
if chiSq > _chiSquaredUniform1DFCritical(pValue) {
|
||||
println("chi-squared test failed: \(trials) \(observedACount) \(chiSq)")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ public func assertionFailure() -> AssertionResult {
|
||||
public func expectTrue(
|
||||
actual: ${BoolType},
|
||||
stackTrace: SourceLocStack? = nil,
|
||||
_ collectMoreInfo: (()->String)? = nil,
|
||||
file: String = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
if !actual {
|
||||
@@ -243,6 +244,7 @@ public func expectTrue(
|
||||
_printStackTrace(stackTrace)
|
||||
println("expected: true")
|
||||
println("actual: \(actual)")
|
||||
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
||||
println()
|
||||
}
|
||||
}
|
||||
@@ -250,6 +252,7 @@ public func expectTrue(
|
||||
public func expectFalse(
|
||||
actual: ${BoolType},
|
||||
stackTrace: SourceLocStack? = nil,
|
||||
_ collectMoreInfo: (()->String)? = nil,
|
||||
file: String = __FILE__, line: UWord = __LINE__
|
||||
) {
|
||||
if actual {
|
||||
@@ -258,6 +261,7 @@ public func expectFalse(
|
||||
_printStackTrace(stackTrace)
|
||||
println("expected: false")
|
||||
println("actual: \(actual)")
|
||||
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,8 +109,8 @@ if let obj: AnyObject = _bridgeToObjectiveC(a3) {
|
||||
}
|
||||
|
||||
// CHECK: dictionary bridges to {
|
||||
// CHECK-NEXT: 1 = Hello;
|
||||
// CHECK-NEXT: 2 = World;
|
||||
// CHECK-NEXT: 1 = Hello;
|
||||
// CHECK-NEXT: }
|
||||
var dict: Dictionary<NSNumber, NSString> = [1: "Hello", 2: "World"]
|
||||
if let obj: AnyObject = _bridgeToObjectiveC(dict) {
|
||||
@@ -120,8 +120,8 @@ if let obj: AnyObject = _bridgeToObjectiveC(dict) {
|
||||
}
|
||||
|
||||
// CHECK: dictionary bridges to {
|
||||
// CHECK-NEXT: 1 = Hello;
|
||||
// CHECK-NEXT: 2 = World;
|
||||
// CHECK-NEXT: 1 = Hello;
|
||||
// CHECK-NEXT: }
|
||||
var dict2 = [1: "Hello", 2: "World"]
|
||||
if let obj: AnyObject = _bridgeToObjectiveC(dict2) {
|
||||
|
||||
@@ -99,8 +99,8 @@ sub(x:f1, y:f2) // CHECK: Int = -1
|
||||
var array = [1, 2, 3, 4, 5]
|
||||
// CHECK: array : [Int] = [1, 2, 3, 4, 5]
|
||||
|
||||
var dict = [ "Hello" : 1.5, "World" : 3.0 ]
|
||||
// CHECK: dict : [String : Double] = ["Hello": 1.5, "World": 3.0]
|
||||
var dict = [ "Hello" : 1.5 ]
|
||||
// CHECK: dict : [String : Double] = ["Hello": 1.5]
|
||||
|
||||
0..<10
|
||||
// FIXME: Disabled CHECK for Range<Int> = 0...10 until we get general printing going
|
||||
|
||||
@@ -370,25 +370,6 @@ println("\(intArrayMirror[0].0): \(intArrayMirror[0].1.summary)")
|
||||
// CHECK-NEXT: [4]: 5
|
||||
println("\(intArrayMirror[4].0): \(intArrayMirror[4].1.summary)")
|
||||
|
||||
let dict = ["One":1,"Two":2,"Three":3,"Four":4,"Five":5]
|
||||
// CHECK-NEXT: ▿ 5 key/value pairs
|
||||
// CHECK-NEXT: ▿ [0]: (2 elements)
|
||||
// CHECK-NEXT: - .0: Four
|
||||
// CHECK-NEXT: - .1: 4
|
||||
// CHECK-NEXT: ▿ [1]: (2 elements)
|
||||
// CHECK-NEXT: - .0: One
|
||||
// CHECK-NEXT: - .1: 1
|
||||
// CHECK-NEXT: ▿ [2]: (2 elements)
|
||||
// CHECK-NEXT: - .0: Two
|
||||
// CHECK-NEXT: - .1: 2
|
||||
// CHECK-NEXT: ▿ [3]: (2 elements)
|
||||
// CHECK-NEXT: - .0: Three
|
||||
// CHECK-NEXT: - .1: 3
|
||||
// CHECK-NEXT: ▿ [4]: (2 elements)
|
||||
// CHECK-NEXT: - .0: Five
|
||||
// CHECK-NEXT: - .1: 5
|
||||
dump(dict)
|
||||
|
||||
enum JustSomeEnum {case A,B}
|
||||
// CHECK-NEXT: (Enum Value)
|
||||
println(reflect(JustSomeEnum.A).summary)
|
||||
|
||||
52
test/stdlib/ReflectionHashing.swift
Normal file
52
test/stdlib/ReflectionHashing.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
// RUN: %target-build-swift -module-name a %s -o %t.out
|
||||
// RUN: %target-run %t.out
|
||||
|
||||
//
|
||||
// This file contains reflection tests that depend on hash values.
|
||||
// Don't add other tests here.
|
||||
//
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
var Reflection = TestCase("Reflection")
|
||||
|
||||
Reflection.test("Dictionary/Empty") {
|
||||
let dict = [Int : Int]()
|
||||
|
||||
var output = ""
|
||||
dump(dict, &output)
|
||||
|
||||
var expected = "- 0 key/value pairs\n"
|
||||
|
||||
expectEqual(expected, output)
|
||||
}
|
||||
|
||||
Reflection.test("Dictionary") {
|
||||
let dict = [ "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5 ]
|
||||
|
||||
var output = ""
|
||||
dump(dict, &output)
|
||||
|
||||
var expected = ""
|
||||
expected += "▿ 5 key/value pairs\n"
|
||||
expected += " ▿ [0]: (2 elements)\n"
|
||||
expected += " - .0: Five\n"
|
||||
expected += " - .1: 5\n"
|
||||
expected += " ▿ [1]: (2 elements)\n"
|
||||
expected += " - .0: Two\n"
|
||||
expected += " - .1: 2\n"
|
||||
expected += " ▿ [2]: (2 elements)\n"
|
||||
expected += " - .0: One\n"
|
||||
expected += " - .1: 1\n"
|
||||
expected += " ▿ [3]: (2 elements)\n"
|
||||
expected += " - .0: Three\n"
|
||||
expected += " - .1: 3\n"
|
||||
expected += " ▿ [4]: (2 elements)\n"
|
||||
expected += " - .0: Four\n"
|
||||
expected += " - .1: 4\n"
|
||||
|
||||
expectEqual(expected, output)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
@@ -858,5 +858,51 @@ Reflection.test("TupleMirror/NoLeak") {
|
||||
}
|
||||
}
|
||||
|
||||
var BitTwiddlingTestCase = TestCase("BitTwiddling")
|
||||
|
||||
func computeCountLeadingZeroes(var x: Int64) -> Int64 {
|
||||
var r: Int64 = 64
|
||||
while x != 0 {
|
||||
x >>= 1
|
||||
r--
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
BitTwiddlingTestCase.test("_countLeadingZeros") {
|
||||
for i in Int64(0)..<1000 {
|
||||
expectEqual(computeCountLeadingZeroes(i), _countLeadingZeros(i))
|
||||
}
|
||||
expectEqual(0, _countLeadingZeros(Int64.min))
|
||||
}
|
||||
|
||||
BitTwiddlingTestCase.test("_isPowerOf2/Int") {
|
||||
expectFalse(_isPowerOf2(Int(-1025)))
|
||||
expectFalse(_isPowerOf2(Int(-1024)))
|
||||
expectFalse(_isPowerOf2(Int(-1023)))
|
||||
expectFalse(_isPowerOf2(Int(-4)))
|
||||
expectFalse(_isPowerOf2(Int(-3)))
|
||||
expectFalse(_isPowerOf2(Int(-2)))
|
||||
expectFalse(_isPowerOf2(Int(-1)))
|
||||
expectFalse(_isPowerOf2(Int(0)))
|
||||
expectTrue(_isPowerOf2(Int(1)))
|
||||
expectTrue(_isPowerOf2(Int(2)))
|
||||
expectFalse(_isPowerOf2(Int(3)))
|
||||
expectTrue(_isPowerOf2(Int(1024)))
|
||||
expectTrue(_isPowerOf2(Int(0x8000_0000)))
|
||||
expectFalse(_isPowerOf2(Int.min))
|
||||
expectFalse(_isPowerOf2(Int.max))
|
||||
}
|
||||
|
||||
BitTwiddlingTestCase.test("_isPowerOf2/UInt") {
|
||||
expectFalse(_isPowerOf2(UInt(0)))
|
||||
expectTrue(_isPowerOf2(UInt(1)))
|
||||
expectTrue(_isPowerOf2(UInt(2)))
|
||||
expectFalse(_isPowerOf2(UInt(3)))
|
||||
expectTrue(_isPowerOf2(UInt(1024)))
|
||||
expectTrue(_isPowerOf2(UInt(0x8000_0000)))
|
||||
expectFalse(_isPowerOf2(UInt.max))
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// RUN: %target-run-simple-swift | FileCheck %s
|
||||
|
||||
func computeCountLeadingZeroes(xi: Int64) -> Int64 {
|
||||
var r : Int64 = 64
|
||||
var x = xi
|
||||
while (x != 0) {
|
||||
x >>= 1
|
||||
r--
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func testeCountLeadingZeroes() {
|
||||
for var i : Int64 = 1; i < 1000; i++ {
|
||||
assert(_countLeadingZeros(i) == computeCountLeadingZeroes(i))
|
||||
}
|
||||
}
|
||||
|
||||
testeCountLeadingZeroes()
|
||||
println("done!") // CHECK: {{^done!$}}
|
||||
116
validation-test/stdlib/Hashing.swift
Normal file
116
validation-test/stdlib/Hashing.swift
Normal file
@@ -0,0 +1,116 @@
|
||||
// RUN: %target-build-swift -Xfrontend -disable-access-control -module-name a %s -o %t.out
|
||||
// RUN: %target-run %t.out
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
var HashingTestCase = TestCase("Hashing")
|
||||
|
||||
HashingTestCase.test("_mixUInt32/GoldenValues") {
|
||||
expectEqual(0x11b882c9, _mixUInt32(0x0))
|
||||
expectEqual(0x60d0aafb, _mixUInt32(0x1))
|
||||
expectEqual(0x636847b5, _mixUInt32(0xffff))
|
||||
expectEqual(0x203f5350, _mixUInt32(0xffff_ffff))
|
||||
|
||||
expectEqual(0xb8747ef6, _mixUInt32(0xa62301f9))
|
||||
expectEqual(0xef4eeeb2, _mixUInt32(0xfe1b46c6))
|
||||
expectEqual(0xd44c9cf1, _mixUInt32(0xe4daf7ca))
|
||||
expectEqual(0xfc1eb1de, _mixUInt32(0x33ff6f5c))
|
||||
expectEqual(0x5605f0c0, _mixUInt32(0x13c2a2b8))
|
||||
expectEqual(0xd9c48026, _mixUInt32(0xf3ad1745))
|
||||
expectEqual(0x471ab8d0, _mixUInt32(0x656eff5a))
|
||||
expectEqual(0xfe265934, _mixUInt32(0xfd2268c9))
|
||||
}
|
||||
|
||||
HashingTestCase.test("_mixInt32/GoldenValues") {
|
||||
expectEqual(Int32(bitPattern: 0x11b882c9), _mixInt32(0x0))
|
||||
}
|
||||
|
||||
HashingTestCase.test("_mixUInt64/GoldenValues") {
|
||||
expectEqual(0xb2b2_4f68_8dc4_164d, _mixUInt64(0x0))
|
||||
expectEqual(0x792e_33eb_0685_57de, _mixUInt64(0x1))
|
||||
expectEqual(0x9ec4_3423_1b42_3dab, _mixUInt64(0xffff))
|
||||
expectEqual(0x4cec_e9c9_01fa_9a84, _mixUInt64(0xffff_ffff))
|
||||
expectEqual(0xcba5_b650_bed5_b87c, _mixUInt64(0xffff_ffff_ffff))
|
||||
expectEqual(0xe583_5646_3fb8_ac99, _mixUInt64(0xffff_ffff_ffff_ffff))
|
||||
|
||||
expectEqual(0xf5d0079f828d43a5, _mixUInt64(0x94ce7d9319f8d233))
|
||||
expectEqual(0x61900a6be9db9c3f, _mixUInt64(0x2728821e8c5b1f7))
|
||||
expectEqual(0xf2fd34b1b7d4b46e, _mixUInt64(0xe7f67ec98c64f482))
|
||||
expectEqual(0x216199ed628c821, _mixUInt64(0xd7c277b5438873ac))
|
||||
expectEqual(0xb1b486ff5f2e0e53, _mixUInt64(0x8399f1d563c42f82))
|
||||
expectEqual(0x61acc92bd91c030, _mixUInt64(0x488cefd48a2c4bfd))
|
||||
expectEqual(0xa7a52d6e4a8e3ddf, _mixUInt64(0x270a15116c351f95))
|
||||
expectEqual(0x98ceedc363c4e56a, _mixUInt64(0xe5fb9b5f6c426a84))
|
||||
}
|
||||
|
||||
HashingTestCase.test("_mixUInt64/GoldenValues") {
|
||||
expectEqual(Int64(bitPattern: 0xb2b2_4f68_8dc4_164d), _mixInt64(0x0))
|
||||
}
|
||||
|
||||
HashingTestCase.test("_mixUInt/GoldenValues") {
|
||||
#if arch(i386) || arch(arm)
|
||||
expectEqual(0x8dc4_164d, _mixUInt(0x0))
|
||||
#elseif arch(x86_64) || arch(arm64)
|
||||
expectEqual(0xb2b2_4f68_8dc4_164d, _mixUInt(0x0))
|
||||
#else
|
||||
fatalError("unimplemented")
|
||||
#endif
|
||||
}
|
||||
|
||||
HashingTestCase.test("_mixInt/GoldenValues") {
|
||||
#if arch(i386) || arch(arm)
|
||||
expectEqual(Int(bitPattern: 0x8dc4_164d), _mixInt(0x0))
|
||||
#elseif arch(x86_64) || arch(arm64)
|
||||
expectEqual(Int(bitPattern: 0xb2b2_4f68_8dc4_164d), _mixInt(0x0))
|
||||
#else
|
||||
fatalError("unimplemented")
|
||||
#endif
|
||||
}
|
||||
|
||||
HashingTestCase.test("_squeezeHashValue/Int") {
|
||||
// Check that the function can return values that cover the whole range.
|
||||
func checkRange(r: Range<Int>) {
|
||||
var results = [Int : Void]()
|
||||
for _ in 0..<(10 * (r.endIndex - r.startIndex)) {
|
||||
let v = _squeezeHashValue(randInt(), r)
|
||||
expectTrue(r ~= v)
|
||||
if results[v] == nil {
|
||||
results[v] = Void()
|
||||
}
|
||||
}
|
||||
expectEqual(results.count, r.endIndex - r.startIndex)
|
||||
}
|
||||
checkRange(Int.min..<(Int.min+10))
|
||||
checkRange(0..<4)
|
||||
checkRange(0..<8)
|
||||
checkRange(-5..<5)
|
||||
checkRange((Int.max-10)..<(Int.max-1))
|
||||
|
||||
// Check that we can handle ranges that span more than `Int.max`.
|
||||
expectEqual(0x32b24f688dc4164d, _squeezeHashValue(0, Int.min..<(Int.max - 1)))
|
||||
expectEqual(-0x6d1cc14f97aa822, _squeezeHashValue(1, Int.min..<(Int.max - 1)))
|
||||
}
|
||||
|
||||
HashingTestCase.test("_squeezeHashValue/UInt") {
|
||||
// Check that the function can return values that cover the whole range.
|
||||
func checkRange(r: Range<UInt>) {
|
||||
var results = [UInt : Void]()
|
||||
let cardinality = r.endIndex - r.startIndex
|
||||
for _ in 0..<(10*cardinality) {
|
||||
let v = _squeezeHashValue(randInt(), r)
|
||||
expectTrue(r ~= v)
|
||||
if results[v] == nil {
|
||||
results[v] = Void()
|
||||
}
|
||||
}
|
||||
expectEqual(results.count, Int(cardinality))
|
||||
}
|
||||
checkRange(0..<4)
|
||||
checkRange(0..<8)
|
||||
checkRange(0..<10)
|
||||
checkRange(10..<20)
|
||||
checkRange((UInt.max-10)..<(UInt.max-1))
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
53
validation-test/stdlib/HashingAvalanche.swift
Normal file
53
validation-test/stdlib/HashingAvalanche.swift
Normal file
@@ -0,0 +1,53 @@
|
||||
// RUN: %target-build-swift -Xfrontend -disable-access-control -module-name a %s -o %t.out -O
|
||||
// RUN: %target-run %t.out
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
var HashingTestCase = TestCase("Hashing")
|
||||
|
||||
func avalancheTest(bits: Int, hashUnderTest: (UInt64) -> UInt64, pValue: Double) {
|
||||
let testsInBatch = 100000
|
||||
let testData = randArray64(testsInBatch)
|
||||
let testDataHashed = Array(lazy(testData).map { hashUnderTest($0) })
|
||||
|
||||
for inputBit in 0..<bits {
|
||||
// Using an array here makes the test too slow.
|
||||
var bitFlips = UnsafeMutablePointer<Int>.alloc(bits)
|
||||
for i in 0..<bits {
|
||||
bitFlips[i] = 0
|
||||
}
|
||||
for i in indices(testData) {
|
||||
let inputA = testData[i]
|
||||
let outputA = testDataHashed[i]
|
||||
let inputB = inputA ^ (1 << UInt64(inputBit))
|
||||
let outputB = hashUnderTest(inputB)
|
||||
var delta = outputA ^ outputB
|
||||
for outputBit in 0..<bits {
|
||||
if delta & 1 == 1 {
|
||||
++bitFlips[outputBit]
|
||||
}
|
||||
delta = delta >> 1
|
||||
}
|
||||
}
|
||||
for outputBit in 0..<bits {
|
||||
expectTrue(
|
||||
chiSquaredUniform2(testsInBatch, bitFlips[outputBit], pValue)) {
|
||||
"inputBit: \(inputBit), outputBit: \(outputBit)"
|
||||
}
|
||||
}
|
||||
bitFlips.dealloc(bits)
|
||||
}
|
||||
}
|
||||
|
||||
// White-box testing: assume that the other N-bit to N-bit mixing functions
|
||||
// just dispatch to these. (Avalanche test is relatively expensive.)
|
||||
HashingTestCase.test("_mixUInt64/avalanche") {
|
||||
avalancheTest(64, _mixUInt64, 0.02)
|
||||
}
|
||||
|
||||
HashingTestCase.test("_mixUInt32/avalanche") {
|
||||
avalancheTest(32, { UInt64(_mixUInt32(UInt32($0 & 0xffff_ffff))) }, 0.02)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
Reference in New Issue
Block a user