Skip to content

Instantly share code, notes, and snippets.

@andersio
Last active September 7, 2016 20:31
Show Gist options
  • Select an option

  • Save andersio/7f8c6805e2349a680178702ed7588514 to your computer and use it in GitHub Desktop.

Select an option

Save andersio/7f8c6805e2349a680178702ed7588514 to your computer and use it in GitHub Desktop.
//
// 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)
}
}
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