Skip to content

Instantly share code, notes, and snippets.

@hartbit
Created February 9, 2016 14:24
Show Gist options
  • Save hartbit/77ee6e5912ab56353b2c to your computer and use it in GitHub Desktop.
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
//
// 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)))
}
}
}
@hartbit
Copy link
Author

hartbit commented Feb 9, 2016

Current results on my iMac:

=== ReactiveCocoa ===
min: 0.302118003368378s
max: 0.327345013618469s
avg: 0.309301480054855s
median: 0.308948040008545s
=== RxSwift ===
min: 0.0297060012817383s
max: 0.0329710245132446s
avg: 0.0309459006786346s
median: 0.0308040380477905s
=== ReactiveKit ===
min: 0.0347309708595276s
max: 1.15560299158096s
avg: 0.581215618848801s
median: 0.61843204498291s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment