//===--- DataBenchmarks.swift ---------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// import TestsUtils import Foundation let d: [BenchmarkCategory] = [.validation, .api, .Data, .cpubench] public let benchmarks = [ BenchmarkInfo(name: "DataCreateEmpty", runFunction: { for _ in 0..<$0*10_000 { blackHole(Data()) } }, tags: d, legacyFactor: 10), BenchmarkInfo(name: "DataCreateSmall", runFunction: { for _ in 0..<$0*10_000 { blackHole(sampleData(.small)) } }, tags: d, legacyFactor: 10), BenchmarkInfo(name: "DataCreateMedium", runFunction: { for _ in 0..<$0*100 { blackHole(sampleData(.medium)) } }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataCreateEmptyArray", runFunction: { for _ in 0..<$0*2_000 { blackHole(Data([])) } }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "DataCreateSmallArray", runFunction: { for _ in 0..<$0*2_000 { blackHole(Data( [0, 1, 2, 3, 4, 5, 6])) } }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "DataCreateMediumArray", runFunction: { for _ in 0..<$0*500 { blackHole(Data([ 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, ])) } }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "Data.init.Sequence.809B.Count", runFunction: { _init($0*100, sequence: Bytes(count: 809, exact: true)) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.809B.Count0", runFunction: { _init($0*100, sequence: Bytes(count: 809, exact: false)) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.809B.Count.I", runFunction: { for _ in 0..<$0*100 { blackHole(Data(Bytes(count: 809, exact: true))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.809B.Count0.I", runFunction: { for _ in 0..<$0*100 { blackHole(Data(Bytes(count: 809, exact: false))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.2047B.Count.I", runFunction: { for _ in 0..<$0*50 { blackHole(Data(Bytes(count: 2047, exact: true))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.2047B.Count0.I", runFunction: { for _ in 0..<$0*50 { blackHole(Data(Bytes(count: 2047, exact: false))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.2049B.Count.I", runFunction: { for _ in 0..<$0*50 { blackHole(Data(Bytes(count: 2049, exact: true))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.2049B.Count0.I", runFunction: { for _ in 0..<$0*50 { blackHole(Data(Bytes(count: 2049, exact: false))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.511B.Count.I", runFunction: { for _ in 0..<$0*150 { blackHole(Data(Bytes(count: 511, exact: true))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.511B.Count0.I", runFunction: { for _ in 0..<$0*150 { blackHole(Data(Bytes(count: 511, exact: false))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.513B.Count.I", runFunction: { for _ in 0..<$0*150 { blackHole(Data(Bytes(count: 513, exact: true))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.513B.Count0.I", runFunction: { for _ in 0..<$0*150 { blackHole(Data(Bytes(count: 513, exact: false))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count", runFunction: { _init($0, sequence: Bytes(count: 2<<15, exact: true)) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0", runFunction: { _init($0, sequence: Bytes(count: 2<<15, exact: false)) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count.I", runFunction: { for _ in 0..<$0 { blackHole(Data(Bytes(count: 2<<15, exact: true))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0.I", runFunction: { for _ in 0..<$0 { blackHole(Data(Bytes(count: 2<<15, exact: false))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.809B.Count.RE", runFunction: { _init($0*100, sequence: repeatElement(UInt8(0xA0), count: 809)) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.809B.Count0.RE", runFunction: { _init($0*100, sequence: Count0(repeatElement(UInt8(0xA0), count: 809))) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.809B.Count.RE.I", runFunction: { for _ in 0..<$0*100 { blackHole(Data(repeatElement(UInt8(0xA0), count: 809))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.809B.Count0.RE.I", runFunction: { for _ in 0..<$0*100 { blackHole(Data(Count0(repeatElement(UInt8(0xA0), count: 809)))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count.RE", runFunction: { _init($0, sequence: repeatElement(UInt8(0xA0), count: 2<<15)) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0.RE", runFunction: { _init($0, sequence: Count0(repeatElement(UInt8(0xA0), count: 2<<15))) }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count.RE.I", runFunction: { for _ in 0..<$0 { blackHole(Data(repeatElement(UInt8(0xA0), count: 2<<15))) } }, tags: d), BenchmarkInfo(name: "Data.init.Sequence.64kB.Count0.RE.I", runFunction: { for _ in 0..<$0 { blackHole(Data(Count0(repeatElement(UInt8(0xA0), count: 2<<15)))) } }, tags: d), BenchmarkInfo(name: "DataSubscriptSmall", runFunction: { let data = small for _ in 0..<$0*10_000 { blackHole(data[1]) } }, tags: d), BenchmarkInfo(name: "DataSubscriptMedium", runFunction: { let data = medium for _ in 0..<$0*10_000 { blackHole(data[521]) } }, tags: d), BenchmarkInfo(name: "DataCountSmall", runFunction: { count($0*10_000, data: small) }, tags: d), BenchmarkInfo(name: "DataCountMedium", runFunction: { count($0*10_000, data: medium) }, tags: d), BenchmarkInfo(name: "DataSetCountSmall", runFunction: { setCount($0*10_000, data: small, extra: 3) }, tags: d), BenchmarkInfo(name: "DataSetCountMedium", runFunction: { setCount($0*1_000, data: medium, extra: 100) }, tags: d, legacyFactor: 10), BenchmarkInfo(name: "DataAccessBytesSmall", runFunction: { withUnsafeBytes($0*10_000, data: small) }, tags: d), BenchmarkInfo(name: "DataAccessBytesMedium", runFunction: { withUnsafeBytes($0*10_000, data: medium) }, tags: d), BenchmarkInfo(name: "DataMutateBytesSmall", runFunction: { withUnsafeMutableBytes($0*500, data: small) }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "DataMutateBytesMedium", runFunction: { withUnsafeMutableBytes($0*500, data: medium) }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "DataCopyBytesSmall", runFunction: { copyBytes($0*10_000, data: small) }, tags: d), BenchmarkInfo(name: "DataCopyBytesMedium", runFunction: { copyBytes($0*5_000, data: medium) }, tags: d, legacyFactor: 2), BenchmarkInfo(name: "DataAppendBytesSmall", runFunction: { append($0*10_000, bytes: 3, to: small) }, tags: d), BenchmarkInfo(name: "DataAppendBytesMedium", runFunction: { append($0*500, bytes: 809, to: medium) }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "DataAppendArray", runFunction: { append($0*100, array: array809, to: medium) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataReset", runFunction: { resetBytes($0*100, in: 431..<809, data: medium) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataReplaceSmall", runFunction: { replace($0*100, data: medium, subrange:431..<809, with: small) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataReplaceMedium", runFunction: { replace($0*100, data: medium, subrange:431..<809, with: medium) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataReplaceLarge", runFunction: { replace($0*100, data: medium, subrange:431..<809, with: large) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataReplaceSmallBuffer", runFunction: { replaceBuffer($0*100, data: medium, subrange:431..<809, with: small) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataReplaceMediumBuffer", runFunction: { replaceBuffer($0*100, data: medium, subrange:431..<809, with: medium) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "DataReplaceLargeBuffer", runFunction: { replaceBuffer($0*100, data: medium, subrange:431..<809, with: large) }, tags: d + [.skip]), // Large buffer replacement is too dependent on the system state // to produce a meaningful benchmark score. 30% variation is common. BenchmarkInfo(name: "DataAppendSequence", runFunction: { append($0*100, sequenceLength: 809, to: medium) }, tags: d, legacyFactor: 100), BenchmarkInfo(name: "Data.append.Sequence.809B.Count", runFunction: { append($0*100, sequence: Bytes(count: 809, exact: true), to: medium) }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.809B.Count0", runFunction: { append($0*100, sequence: Bytes(count: 809, exact: false) , to: medium) }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.809B.Count.I", runFunction: { for _ in 1...$0*100 { var copy = medium copy.append(contentsOf: Bytes(count: 809, exact: true)) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.809B.Count0.I", runFunction: { for _ in 1...$0*100 { var copy = medium copy.append(contentsOf: Bytes(count: 809, exact: false)) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count", runFunction: { append($0, sequence: Bytes(count: 2<<15, exact: true), to: medium) }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0", runFunction: { append($0, sequence: Bytes(count: 2<<15, exact: false), to: medium) }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count.I", runFunction: { for _ in 1...$0 { var copy = medium copy.append(contentsOf: Bytes(count: 2<<15, exact: true)) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0.I", runFunction: { for _ in 1...$0 { var copy = medium copy.append(contentsOf: Bytes(count: 2<<15, exact: false)) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.809B.Count.RE.I", runFunction: { for _ in 1...$0*100 { var copy = medium copy.append(contentsOf: repeatElement(UInt8(0xA0), count: 809)) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.809B.Count0.RE.I", runFunction: { for _ in 1...$0*100 { var copy = medium copy.append(contentsOf: Count0(repeatElement(UInt8(0xA0), count: 809))) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.809B.Count.RE", runFunction: { append($0*100, sequence: repeatElement(UInt8(0xA0), count: 809), to: medium) }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.809B.Count0.RE", runFunction: { append($0*100, sequence: Count0(repeatElement(UInt8(0xA0), count: 809)), to: medium) }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count.RE.I", runFunction: { for _ in 1...$0 { var copy = medium copy.append(contentsOf: repeatElement(UInt8(0xA0), count: 2<<15)) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0.RE.I", runFunction: { for _ in 1...$0 { var copy = medium copy.append(contentsOf: Count0(repeatElement(UInt8(0xA0), count: 2<<15))) } }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count.RE", runFunction: { append($0, sequence: repeatElement(UInt8(0xA0), count: 2<<15), to: medium) }, tags: d), BenchmarkInfo(name: "Data.append.Sequence.64kB.Count0.RE", runFunction: { append($0, sequence: Count0(repeatElement(UInt8(0xA0), count: 2<<15)), to: medium) }, tags: d), BenchmarkInfo(name: "DataAppendDataSmallToSmall", runFunction: { append($0*500, data: small, to: small) }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "DataAppendDataSmallToMedium", runFunction: { append($0*500, data: small, to: medium) }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "DataAppendDataSmallToLarge", runFunction: { append($0*50, data: small, to: large) }, tags: d, legacyFactor: 200), BenchmarkInfo(name: "DataAppendDataMediumToSmall", runFunction: { append($0*500, data: medium, to: small) }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "DataAppendDataMediumToMedium", runFunction: { append($0*500, data: medium, to: medium) }, tags: d, legacyFactor: 20), BenchmarkInfo(name: "DataAppendDataMediumToLarge", runFunction: { append($0*50, data: medium, to: large) }, tags: d, legacyFactor: 200), BenchmarkInfo(name: "DataAppendDataLargeToSmall", runFunction: { append($0*50, data: large, to: small) }, tags: d, legacyFactor: 200), BenchmarkInfo(name: "DataAppendDataLargeToMedium", runFunction: { append($0*50, data: large, to: medium) }, tags: d, legacyFactor: 200), BenchmarkInfo(name: "DataAppendDataLargeToLarge", runFunction: { append($0*50, data: large, to: large) }, tags: d + [.unstable], legacyFactor: 200), BenchmarkInfo(name: "DataToStringEmpty", runFunction: { string($0*200, from: emptyData) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "DataToStringSmall", runFunction: { string($0*200, from: smallData) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "DataToStringMedium", runFunction: { string($0*200, from: mediumData) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "DataToStringLargeUnicode", runFunction: { string($0*200, from: largeUnicodeData) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "StringToDataEmpty", runFunction: { dataFromUTF8View($0*200, from: emptyString) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "StringToDataSmall", runFunction: { dataFromUTF8View($0*200, from: smallString) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "StringToDataMedium", runFunction: { dataFromUTF8View($0*200, from: mediumString) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "StringToDataLargeUnicode", runFunction: { dataFromUTF8View($0*200, from: largeUnicodeString) }, tags: d, legacyFactor: 50), BenchmarkInfo(name: "String.data.Empty", runFunction: { dataUsingUTF8Encoding($0*200, from: emptyString) }, tags: d), BenchmarkInfo(name: "String.data.Small", runFunction: { dataUsingUTF8Encoding($0*200, from: smallString) }, tags: d), BenchmarkInfo(name: "String.data.Medium", runFunction: { dataUsingUTF8Encoding($0*200, from: mediumString) }, tags: d), BenchmarkInfo(name: "String.data.LargeUnicode", runFunction: { dataUsingUTF8Encoding($0*200, from: largeUnicodeString) }, tags: d), BenchmarkInfo(name: "Data.hash.Empty", runFunction: { hash($0*10_000, data: Data()) }, tags: d), BenchmarkInfo(name: "Data.hash.Small", runFunction: { hash($0*10_000, data: small) }, tags: d), BenchmarkInfo(name: "Data.hash.Medium", runFunction: { hash($0*1_000, data: medium) }, tags: d), ] let emptyString = "" let smallString = "\r\n" let mediumString = "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n" let largeUnicodeString = "Swiftに大幅な改良が施され、𓀀𓀁𓀂𓀃, 🇺🇸🇨🇦🇲🇽" + mediumString let emptyData = Data() let smallData = Data(smallString.utf8) let mediumData = Data(mediumString.utf8) let largeUnicodeData = Data(largeUnicodeString.utf8) let small = sampleData(.small) let medium = sampleData(.medium) let large = sampleData(.large) let array809 = byteArray(size: 809) struct Count0 : Sequence { let base: S init (_ base:S) { self.base = base } func makeIterator() -> S.Iterator { return base.makeIterator() } var underestimatedCount: Int { return 0 } } struct Bytes: Sequence, IteratorProtocol { let count: Int let exact: Bool var i: Int = 0 init(count: Int, exact: Bool) { self.count = count self.exact = exact } mutating func next() -> UInt8? { defer { i = i &+ 1 } return (i < count) ? UInt8(truncatingIfNeeded: i) : nil } var underestimatedCount: Int { return exact ? count : 0 } } enum SampleKind { case small case medium case large case veryLarge case immutableBacking } func byteArray(size: Int) -> [UInt8] { var bytes = [UInt8](repeating: 0, count: size) bytes.withUnsafeMutableBufferPointer { buffer in for i in buffer.indices { buffer[i] = UInt8(truncatingIfNeeded: i) } } return bytes } func sampleData(size: Int) -> Data { var data = Data(count: size) data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> () in for i in 0.. Data { let count = 1033 let data = NSData(bytes: byteArray(size: count), length: count) return Data(referencing: data) } func sampleData(_ type: SampleKind) -> Data { switch type { case .small: return sampleData(size: 11) case .medium: return sampleData(size: 1033) case .large: return sampleData(size: 40980) case .veryLarge: return sampleData(size: 1024 * 1024 * 1024 + 128) case .immutableBacking: return sampleBridgedNSData() } } @inline(never) func withUnsafeBytes(_ n: Int, data: Data) { for _ in 1...n { data.withUnsafeBytes { (ptr: UnsafePointer) in blackHole(ptr.pointee) } } } @inline(never) func withUnsafeMutableBytes(_ n: Int, data: Data) { for _ in 1...n { var copy = data copy.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) in // Mutate a byte ptr.pointee = 42 } } } @inline(never) func copyBytes(_ n: Int, data: Data) { let amount = data.count let buffer = UnsafeMutablePointer.allocate(capacity: amount) defer { buffer.deallocate() } for _ in 1...n { data.copyBytes(to: buffer, from: 0..(_ n: Int, sequence: S, to data: Data) where S.Element == UInt8 { for _ in 1...n { var copy = data copy.append(contentsOf: sequence) } } @inline(never) func _init(_ n: Int, sequence: S) where S.Element == UInt8 { for _ in 1...n { blackHole(Data(sequence)) } } @inline(never) func resetBytes(_ n: Int, in range: Range, data: Data) { for _ in 1...n { var copy = data copy.resetBytes(in: range) } } @inline(never) func replace( _ n: Int, data: Data, subrange range: Range, with replacement: Data ) { for _ in 1...n { var copy = data copy.replaceSubrange(range, with: replacement) } } @inline(never) func replaceBuffer( _ n: Int, data: Data, subrange range: Range, with replacement: Data ) { replacement.withUnsafeBytes { (bytes: UnsafePointer) in let buffer = UnsafeBufferPointer(start: bytes, count: replacement.count) for _ in 1...n { var copy = data copy.replaceSubrange(range, with: buffer) } } } @inline(never) func append(_ n: Int, data: Data, to target: Data) { var copy: Data for _ in 1...n { copy = target copy.append(data) } } @inline(never) public func count(_ n: Int, data: Data) { for _ in 1...n { blackHole(data.count) } } @inline(never) public func setCount(_ n: Int, data: Data, extra: Int) { var copy = data let count = data.count + extra let orig = data.count for _ in 1...n { copy.count = count copy.count = orig } } @inline(never) public func string(_ n: Int, from data: Data) { for _ in 1...n { blackHole(String(decoding: data, as: UTF8.self)) } } @inline(never) public func dataFromUTF8View(_ n: Int, from string: String) { for _ in 1...n { blackHole(Data(string.utf8)) } } @inline(never) public func dataUsingUTF8Encoding(_ n: Int, from string: String) { for _ in 1...n { autoreleasepool { blackHole(string.data(using: .utf8)) } } } @inline(never) public func hash(_ n: Int, data: Data) { var hasher = Hasher() for _ in 0 ..< n { hasher.combine(data) } let _ = hasher.finalize() }