Created
February 9, 2016 14:24
-
-
Save hartbit/77ee6e5912ab56353b2c to your computer and use it in GitHub Desktop.
Testing a performance scenario of many observers in different Reactive frameworks in Swift
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
// | |
// main.swift | |
// ReactivePerf | |
// | |
// Created by David Hart on 09/02/16. | |
// Copyright © 2016 David Hart. All rights reserved. | |
// | |
import Foundation | |
import enum Result.NoError | |
//MARK: Helpers | |
func benchmark(samples: Int = 50, operation: () -> Void) { | |
var sampleTimes: [NSTimeInterval] = [] | |
for _ in 0..<samples { | |
autoreleasepool { | |
let startDate = NSDate() | |
operation() | |
sampleTimes.append(abs(startDate.timeIntervalSinceNow)) | |
} | |
} | |
sampleTimes.sortInPlace() | |
print("min: \(sampleTimes.minElement()!)s") | |
print("max: \(sampleTimes.maxElement()!)s") | |
print("avg: \(sampleTimes.reduce(0.0, combine: +) / Double(samples))s") | |
print("median: \(sampleTimes[samples/2])s") | |
} | |
public func + (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber { | |
return lhs.decimalNumberByAdding(rhs) | |
} | |
public func - (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber { | |
return lhs.decimalNumberBySubtracting(rhs) | |
} | |
public func * (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber { | |
return lhs.decimalNumberByMultiplyingBy(rhs) | |
} | |
public func / (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber { | |
return lhs.decimalNumberByDividingBy(rhs) | |
} | |
let branching = 10 | |
let boughCount = branching | |
let branchCount = boughCount * branching | |
let leafCount = branchCount * branching | |
//MARK: ReactiveCocoa | |
import ReactiveCocoa | |
print("=== ReactiveCocoa ===") | |
autoreleasepool { | |
let rac_leaves = (0..<leafCount).map { _ in ReactiveCocoa.MutableProperty(NSDecimalNumber.zero()) } | |
let rac_branches = (0..<branchCount).map { index -> ReactiveCocoa.SignalProducer<NSDecimalNumber, NoError> in | |
let leafIndex = index * branching | |
let leaveProducers = rac_leaves[leafIndex..<leafIndex+branching].map { $0.producer } | |
return ReactiveCocoa.combineLatest(leaveProducers).map { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: +) | |
} | |
} | |
let rac_boughs = (0..<10).map { index -> ReactiveCocoa.SignalProducer<NSDecimalNumber, NoError> in | |
let branchIndex = index * branching | |
return ReactiveCocoa.combineLatest(rac_branches[branchIndex..<branchIndex+branching]).map { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: *) | |
} | |
} | |
let rac_trunk = ReactiveCocoa.combineLatest(rac_boughs).map { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: +) / NSDecimalNumber(integer: 10) | |
} | |
benchmark { | |
let disposable = rac_trunk.startWithNext { finalValue in | |
} | |
for _ in (0..<1000) { | |
let leaf = rac_leaves[Int(arc4random_uniform(UInt32(leafCount)))] | |
leaf.value = NSDecimalNumber(integer: Int(arc4random_uniform(100))) | |
} | |
disposable.dispose() | |
} | |
} | |
//MARK: RxSwift | |
import RxSwift | |
print("=== RxSwift ===") | |
autoreleasepool { | |
let rx_leaves = (0..<leafCount).map { _ in RxSwift.Variable(NSDecimalNumber.zero()) } | |
let rx_branches = (0..<branchCount).map { (index: Int) -> RxSwift.Observable<NSDecimalNumber> in | |
let leafIndex = index * branching | |
return rx_leaves[leafIndex..<leafIndex+branching] | |
.map { $0.asObservable() } | |
.combineLatest { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: +) | |
} | |
} | |
let rx_boughs = (0..<10).map { (index: Int) -> RxSwift.Observable<NSDecimalNumber> in | |
let branchIndex = index * branching | |
return rx_branches[branchIndex..<branchIndex+branching] | |
.map { $0.asObservable() } | |
.combineLatest { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: *) | |
} | |
} | |
let rx_trunk = rx_boughs.combineLatest { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: +) / NSDecimalNumber(integer: 10) | |
} | |
benchmark { | |
let disposable = rx_trunk.subscribeNext { finalValue in | |
} | |
for _ in (0..<1000) { | |
let leaf = rx_leaves[Int(arc4random_uniform(UInt32(leafCount)))] | |
leaf.value = NSDecimalNumber(integer: Int(arc4random_uniform(100))) | |
} | |
disposable.dispose() | |
} | |
} | |
//MARK: ReactiveKit | |
import ReactiveKit | |
print("=== ReactiveKit ===") | |
extension CollectionType where Generator.Element: StreamType { | |
@warn_unused_result | |
public func combineLatest() -> ReactiveKit.Stream<[Generator.Element.Event]> { | |
return create { observer in | |
let queue = Queue(name: "com.ReactiveKit.ReactiveKit.CombineLatestWith") | |
var events: [Generator.Element.Event?] = self.map { _ in return Optional<Generator.Element.Event>() } | |
let dispatchIfPossible = { () -> () in | |
var flattenedEvents: [Generator.Element.Event] = [] | |
for event in events { | |
if let event = event { | |
flattenedEvents.append(event) | |
} else { | |
return | |
} | |
} | |
observer(flattenedEvents) | |
} | |
return ReactiveKit.CompositeDisposable(self.enumerate().map { index, stream in | |
stream.observe(on: nil) { event in | |
queue.sync { | |
events[index] = event | |
dispatchIfPossible() | |
} | |
} | |
}) | |
} | |
} | |
} | |
autoreleasepool { | |
let rek_leaves = (0..<leafCount).map { _ in ReactiveKit.Observable(NSDecimalNumber.zero()) } | |
let rek_branches = (0..<branchCount).map { (index: Int) -> ReactiveKit.Stream<NSDecimalNumber> in | |
let leafIndex = index * branching | |
return rek_leaves[leafIndex..<leafIndex+branching].combineLatest().map { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: +) | |
} | |
} | |
let rek_boughs = (0..<10).map { (index: Int) -> ReactiveKit.Stream<NSDecimalNumber> in | |
let branchIndex = index * 10 | |
return rek_branches[branchIndex..<branchIndex+branching].combineLatest().map { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: *) | |
} | |
} | |
let rek_trunk = rek_boughs.combineLatest().map { values in | |
return values.reduce(NSDecimalNumber.zero(), combine: +) / NSDecimalNumber(integer: 10) | |
} | |
benchmark { | |
rek_trunk.observe { finalValue in | |
} | |
for _ in (0..<1000) { | |
let leaf = rek_leaves[Int(arc4random_uniform(UInt32(leafCount)))] | |
leaf.value = NSDecimalNumber(integer: Int(arc4random_uniform(100))) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Current results on my iMac: