Last active
September 7, 2016 20:31
-
-
Save andersio/7f8c6805e2349a680178702ed7588514 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Benchmarks.swift | |
| // Benchmarks | |
| // | |
| // Created by Anders on 6/9/2016. | |
| // Copyright © 2016 Ik ben anders. All rights reserved. | |
| // | |
| import XCTest | |
| public final class AnySignal<Value>: SignalProtocol { | |
| let _observe: (@escaping (Value) -> Void) -> Void | |
| init<U: _SignalProtocolBase>(_ base: U) where U.Value == Value { | |
| _observe = base.observe | |
| } | |
| public func observe(_ action: @escaping (Value) -> Void) { | |
| _observe(action) | |
| } | |
| } | |
| public protocol _SignalProtocolBase: class { | |
| associatedtype Value | |
| func observe(_ action: @escaping (Value) -> ()) | |
| } | |
| public protocol _SignalMappingSource: class, _SignalProtocolBase { | |
| } | |
| public protocol SignalProtocol: _SignalMappingSource {} | |
| extension SignalProtocol { | |
| public func erasingMap<U>(_ transform: @escaping (Value) -> U) -> Signal<U> { | |
| return Signal { inputObserver in | |
| self.observe { value in | |
| inputObserver(transform(value)) | |
| } | |
| } | |
| } | |
| public func encodedMap<U>(_ transform: @escaping (Value) -> U) -> MappedSignal<U, Self> { | |
| return MappedSignal(source: self, transform: transform) | |
| } | |
| public func encodedWrappedMap<U>(_ transform: @escaping (Value) -> U) -> AnySignal<U> { | |
| return AnySignal(MappedSignal(source: self, transform: transform)) | |
| } | |
| } | |
| extension _SignalMappingSource { | |
| public func optimizedEncodedMap<U>(_ transform: @escaping (Value) -> U) -> OptimizedMappedSignal<U, Value> { | |
| return OptimizedMappedSignal(observeOriginal: self.observe, transform: transform) | |
| } | |
| } | |
| public protocol _SignalMappingIntermediate: class, _SignalProtocolBase { | |
| associatedtype Original | |
| } | |
| extension _SignalMappingIntermediate { | |
| public func optimizedEncodedMap<U>(_ transform: @escaping (Value) -> U) -> OptimizedMappedSignal<U, Original> { | |
| let mapped = self as! OptimizedMappedSignal<Value, Original> | |
| return OptimizedMappedSignal(observeOriginal: mapped.observeOriginal, | |
| transform: { transform(mapped.transform($0)) }) | |
| } | |
| } | |
| public final class Signal<Value>: SignalProtocol { | |
| private var observers: [(Value) -> Void] = [] | |
| public init(_ generator: (@escaping (Value) -> Void) -> Void) { | |
| generator { value in | |
| self.observers.forEach { $0(value) } | |
| } | |
| } | |
| public func observe(_ action: @escaping (Value) -> Void) { | |
| observers.append(action) | |
| } | |
| } | |
| public final class MappedSignal<Value, _Signal: SignalProtocol>: SignalProtocol { | |
| public typealias Original = _Signal.Value | |
| private let transform: (_Signal.Value) -> Value | |
| private let signal: _Signal | |
| private let lock = NSLock() | |
| private var lazySignal: Signal<Value>? | |
| public init(source: _Signal, transform: @escaping (_Signal.Value) -> Value) { | |
| self.transform = transform | |
| self.signal = source | |
| } | |
| public func observe(_ action: @escaping (Value) -> ()) { | |
| lock.lock() | |
| defer { lock.unlock() } | |
| if lazySignal == nil { | |
| lazySignal = Signal { observer in signal.observe { observer(self.transform($0)) } } | |
| } | |
| lazySignal!.observe(action) | |
| } | |
| } | |
| public final class OptimizedMappedSignal<Value, _Original>: _SignalProtocolBase, _SignalMappingIntermediate { | |
| public typealias Original = _Original | |
| fileprivate let transform: (_Original) -> Value | |
| fileprivate let observeOriginal: (@escaping (_Original) -> Void) -> Void | |
| private let lock = NSLock() | |
| private var lazySignal: Signal<Value>? | |
| public init(observeOriginal: @escaping (@escaping (_Original) -> Void) -> Void, transform: @escaping (_Original) -> Value) { | |
| self.transform = transform | |
| self.observeOriginal = observeOriginal | |
| } | |
| public func observe(_ action: @escaping (Value) -> Void) { | |
| lock.lock() | |
| defer { lock.unlock() } | |
| if lazySignal == nil { | |
| lazySignal = Signal { observer in observeOriginal { observer(self.transform($0)) } } | |
| } | |
| lazySignal!.observe(action) | |
| } | |
| } | |
| class Benchmarks: XCTestCase { | |
| func testErasedSignal() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .erasingMap { $0 + 10 * 1 } | |
| .erasingMap { $0 + 10 * 2 } | |
| .erasingMap { $0 + 10 * 3 } | |
| .erasingMap { $0 + 10 * 4 } | |
| .erasingMap { $0 + 10 * 5 } | |
| .erasingMap { $0 + 10 * 6 } | |
| .erasingMap { $0 + 10 * 7 } | |
| .erasingMap { $0 + 10 * 8 } | |
| .erasingMap { $0 + 10 * 9 } | |
| .erasingMap { $0 + 10 * 10 } | |
| var value = -1 | |
| AnySignal(mappedSignal).observe { value = $0 } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| func testEncodedSignal() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .encodedMap { $0 + 10 * 1 } | |
| .encodedMap { $0 + 10 * 2 } | |
| .encodedMap { $0 + 10 * 3 } | |
| .encodedMap { $0 + 10 * 4 } | |
| .encodedMap { $0 + 10 * 5 } | |
| .encodedMap { $0 + 10 * 6 } | |
| .encodedMap { $0 + 10 * 7 } | |
| .encodedMap { $0 + 10 * 8 } | |
| .encodedMap { $0 + 10 * 9 } | |
| .encodedMap { $0 + 10 * 10 } | |
| var value = -1 | |
| AnySignal(mappedSignal).observe { value = $0 } | |
| //mappedSignal.observe { value = $0 } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| func testEncodedWrappedSignal() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .encodedWrappedMap { $0 + 10 * 1 } | |
| .encodedWrappedMap { $0 + 10 * 2 } | |
| .encodedWrappedMap { $0 + 10 * 3 } | |
| .encodedWrappedMap { $0 + 10 * 4 } | |
| .encodedWrappedMap { $0 + 10 * 5 } | |
| .encodedWrappedMap { $0 + 10 * 6 } | |
| .encodedWrappedMap { $0 + 10 * 7 } | |
| .encodedWrappedMap { $0 + 10 * 8 } | |
| .encodedWrappedMap { $0 + 10 * 9 } | |
| .encodedWrappedMap { $0 + 10 * 10 } | |
| var value = -1 | |
| AnySignal(mappedSignal).observe { value = $0 } | |
| //mappedSignal.observe { value = $0 } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| func testEncodedOptimizedSignal() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .optimizedEncodedMap { $0 + 10 * 1 } | |
| .optimizedEncodedMap { $0 + 10 * 2 } | |
| .optimizedEncodedMap { $0 + 10 * 3 } | |
| .optimizedEncodedMap { $0 + 10 * 4 } | |
| .optimizedEncodedMap { $0 + 10 * 5 } | |
| .optimizedEncodedMap { $0 + 10 * 6 } | |
| .optimizedEncodedMap { $0 + 10 * 7 } | |
| .optimizedEncodedMap { $0 + 10 * 8 } | |
| .optimizedEncodedMap { $0 + 10 * 9 } | |
| .optimizedEncodedMap { $0 + 10 * 10 } | |
| var value = -1 | |
| AnySignal(mappedSignal).observe { value = $0 } | |
| //mappedSignal.observe { value = $0 } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| // More subscribers. | |
| func testErasedSignalMoreSubscribers() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .erasingMap { $0 + 10 * 1 } | |
| .erasingMap { $0 + 10 * 2 } | |
| .erasingMap { $0 + 10 * 3 } | |
| .erasingMap { $0 + 10 * 4 } | |
| .erasingMap { $0 + 10 * 5 } | |
| .erasingMap { $0 + 10 * 6 } | |
| .erasingMap { $0 + 10 * 7 } | |
| .erasingMap { $0 + 10 * 8 } | |
| .erasingMap { $0 + 10 * 9 } | |
| .erasingMap { $0 + 10 * 10 } | |
| var value = -1 | |
| let wrapped = AnySignal(mappedSignal) | |
| wrapped.observe { value = $0 } | |
| for _ in 2 ... 20 { | |
| wrapped.observe { value = $0 } | |
| } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| func testEncodedSignalMoreSubscribers() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .encodedMap { $0 + 10 * 1 } | |
| .encodedMap { $0 + 10 * 2 } | |
| .encodedMap { $0 + 10 * 3 } | |
| .encodedMap { $0 + 10 * 4 } | |
| .encodedMap { $0 + 10 * 5 } | |
| .encodedMap { $0 + 10 * 6 } | |
| .encodedMap { $0 + 10 * 7 } | |
| .encodedMap { $0 + 10 * 8 } | |
| .encodedMap { $0 + 10 * 9 } | |
| .encodedMap { $0 + 10 * 10 } | |
| var value = -1 | |
| let wrapped = AnySignal(mappedSignal) | |
| wrapped.observe { value = $0 } | |
| for _ in 2 ... 20 { | |
| wrapped.observe { value = $0 } | |
| } | |
| //mappedSignal.observe { value = $0 } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| func testEncodedWrappedSignalMoreSubscribers() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .encodedWrappedMap { $0 + 10 * 1 } | |
| .encodedWrappedMap { $0 + 10 * 2 } | |
| .encodedWrappedMap { $0 + 10 * 3 } | |
| .encodedWrappedMap { $0 + 10 * 4 } | |
| .encodedWrappedMap { $0 + 10 * 5 } | |
| .encodedWrappedMap { $0 + 10 * 6 } | |
| .encodedWrappedMap { $0 + 10 * 7 } | |
| .encodedWrappedMap { $0 + 10 * 8 } | |
| .encodedWrappedMap { $0 + 10 * 9 } | |
| .encodedWrappedMap { $0 + 10 * 10 } | |
| var value = -1 | |
| let wrapped = AnySignal(mappedSignal) | |
| wrapped.observe { value = $0 } | |
| for _ in 2 ... 20 { | |
| wrapped.observe { value = $0 } | |
| } | |
| //mappedSignal.observe { value = $0 } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| func testEncodedOptimizedSignalMoreSubscribers() { | |
| var inputObserver: ((Int) -> Void)! | |
| let signal = Signal<Int> { inputObserver = $0 } | |
| let mappedSignal = signal | |
| .optimizedEncodedMap { $0 + 10 * 1 } | |
| .optimizedEncodedMap { $0 + 10 * 2 } | |
| .optimizedEncodedMap { $0 + 10 * 3 } | |
| .optimizedEncodedMap { $0 + 10 * 4 } | |
| .optimizedEncodedMap { $0 + 10 * 5 } | |
| .optimizedEncodedMap { $0 + 10 * 6 } | |
| .optimizedEncodedMap { $0 + 10 * 7 } | |
| .optimizedEncodedMap { $0 + 10 * 8 } | |
| .optimizedEncodedMap { $0 + 10 * 9 } | |
| .optimizedEncodedMap { $0 + 10 * 10 } | |
| var value = -1 | |
| let wrapped = AnySignal(mappedSignal) | |
| wrapped.observe { value = $0 } | |
| for _ in 2 ... 20 { | |
| wrapped.observe { value = $0 } | |
| } | |
| //mappedSignal.observe { value = $0 } | |
| self.measure { | |
| for i in 1 ... 100000 { | |
| inputObserver!(i) | |
| } | |
| } | |
| XCTAssert(value == 100550) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Test Case '-[Benchmarks.Benchmarks testEncodedOptimizedSignal]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:248: Test Case '-[Benchmarks.Benchmarks testEncodedOptimizedSignal]' measured [Time, seconds] average: 0.071, relative standard deviation: 2.979%, values: [0.069503, 0.072905, 0.072445, 0.074348, 0.070250, 0.069254, 0.066715, 0.069233, 0.071738, 0.071388], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testEncodedOptimizedSignal]' passed (1.022 seconds). | |
| Test Case '-[Benchmarks.Benchmarks testEncodedSignal]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:190: Test Case '-[Benchmarks.Benchmarks testEncodedSignal]' measured [Time, seconds] average: 0.166, relative standard deviation: 5.036%, values: [0.188412, 0.168682, 0.162733, 0.160864, 0.167874, 0.162526, 0.166839, 0.162050, 0.155370, 0.163423], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testEncodedSignal]' passed (1.912 seconds). | |
| Test Case '-[Benchmarks.Benchmarks testEncodedWrappedSignal]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:219: Test Case '-[Benchmarks.Benchmarks testEncodedWrappedSignal]' measured [Time, seconds] average: 0.164, relative standard deviation: 3.178%, values: [0.175791, 0.161295, 0.159014, 0.160624, 0.168611, 0.167023, 0.164181, 0.159204, 0.160686, 0.158974], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testEncodedWrappedSignal]' passed (1.887 seconds). | |
| Test Case '-[Benchmarks.Benchmarks testErasedSignal]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:161: Test Case '-[Benchmarks.Benchmarks testErasedSignal]' measured [Time, seconds] average: 0.147, relative standard deviation: 3.284%, values: [0.159459, 0.148396, 0.146777, 0.148026, 0.143902, 0.146324, 0.149008, 0.143399, 0.145325, 0.140270], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testErasedSignal]' passed (1.724 seconds). | |
| # Test cases with 20 subscribers | |
| Test Case '-[Benchmarks.Benchmarks testEncodedOptimizedSignalMoreSubscribers]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:381: Test Case '-[Benchmarks.Benchmarks testEncodedOptimizedSignalMoreSubscribers]' measured [Time, seconds] average: 0.183, relative standard deviation: 2.824%, values: [0.182194, 0.183402, 0.177719, 0.184296, 0.185273, 0.194525, 0.189742, 0.179530, 0.179519, 0.177398], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testEncodedOptimizedSignalMoreSubscribers]' passed (2.087 seconds). | |
| Test Case '-[Benchmarks.Benchmarks testEncodedSignalMoreSubscribers]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:315: Test Case '-[Benchmarks.Benchmarks testEncodedSignalMoreSubscribers]' measured [Time, seconds] average: 0.289, relative standard deviation: 4.393%, values: [0.307507, 0.270349, 0.293850, 0.311213, 0.297452, 0.274339, 0.283434, 0.284821, 0.280487, 0.290924], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testEncodedSignalMoreSubscribers]' passed (3.148 seconds). | |
| Test Case '-[Benchmarks.Benchmarks testEncodedWrappedSignalMoreSubscribers]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:348: Test Case '-[Benchmarks.Benchmarks testEncodedWrappedSignalMoreSubscribers]' measured [Time, seconds] average: 0.279, relative standard deviation: 3.296%, values: [0.302621, 0.287484, 0.278478, 0.280153, 0.273468, 0.272177, 0.280261, 0.272075, 0.274890, 0.270496], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testEncodedWrappedSignalMoreSubscribers]' passed (3.044 seconds). | |
| Test Case '-[Benchmarks.Benchmarks testErasedSignalMoreSubscribers]' started. | |
| /Users/anders/Repositories/GLN/Benchmarks/Benchmarks.swift:282: Test Case '-[Benchmarks.Benchmarks testErasedSignalMoreSubscribers]' measured [Time, seconds] average: 0.268, relative standard deviation: 2.936%, values: [0.277123, 0.271084, 0.265696, 0.259874, 0.268067, 0.265600, 0.251432, 0.273353, 0.279885, 0.270641], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 | |
| Test Case '-[Benchmarks.Benchmarks testErasedSignalMoreSubscribers]' passed (2.936 seconds). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment