Profiling declarative style code (Rx/ReactiveCocoa)

tools
profiling
reactive
rxswift

#1

This is half-way between a query and a feature enhancement. I don't think this capability exists but would quite happily by shown an alternative. Really, I'm hoping someone who understands these things better can help lock down a descriptive feature request to be made of either Swift or the Xcode Instruments team at Apple.

I'm currently working with RxSwift. Generally, It's been a pleasure to work with but I find it lacking in one very important area: performance tuning.

As anyone who has ever tried profiling any Rx code will know, the time profiler is pretty much useless as the callstack limit is quickly exhausted. Even if you're lucky enough not to exhaust the callstack, the nature of a declarative API such as Rx means that you're left with a bunch of 'anonymous closures' which don't seem to be grouped in any useful way, which makes it difficult to identify the heaviest code paths.

What I would like is a way to group anonymous closures in the scope that they were defined in. i.e. if I have a method that has some declarative code/binds to an Rx stream:

private func bindViewToPosition(source: Observable <State>, disposeBag: DisposeBag) {
    let floatingIndex = source.map(keyPath: \.index).distinctUntilChanged(==)
    return view.rx.isScrolling
        .flatMapLatest { isScrolling in isScrolling ? .never() : floatingIndex }
        .subscribe(onNext: { self.updateSelectedPosition(floatingIndex) })
        .disposed(by: disposeBag)
}

One solution to the problem might be if, in the time profiler, it was possible to select a mode which somehow relates the anonymous closure in the subscribe call, with the enclosing scope of bindViewToPosition. Is this even possible technically? Is this something that Swift would be able to accommodate? Would Swift need updating to support this or is this something the Instruments team could handle in isolation?

So in summary: better tooling for declarative APIs (specifically Rx/ReactiveSwift – but I imagine there are many more cases where this problem applies). Is it possible? Can Swift do it?


(Tino) #2

Imho when you are using one of the so called FRP-toolkits, it's better not to focus on performance, but rather on code that's easy to comprehend - because when you want maximum speed, other approaches are significantly better anyways (at least that was the case when I compared the last time).

But maybe https://developer.apple.com/videos/play/wwdc2018/405/ illustrates some techniques you can adopt.