Repository: airbnb/lottie-ios
File: Tests/PerformanceTests.swift (Swift)

Code before:
  private func compareEngineScrubbingPerformance(for animation: Animation, iterations: Int) -> Double {
    let mainThreadAnimationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: .mainThread))
    let mainThreadEnginePerformance = measurePerformance {
      for i in 0 ..< iterations {
        mainThreadAnimationView.currentProgress = Double(i) / Double(iterations)

        // Since the main thread engine only re-renders in `display()`, which is normally called by Core Animation,
        // we have to invoke it manually. This triggers the same code path that would happen when scrubbing in
        // a real use case.
        mainThreadAnimationView.animationLayer!.display()
      }
    }

    print("Main thread engine took \(mainThreadEnginePerformance) seconds")

    let coreAnimationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: .coreAnimation))
    let coreAnimationEnginePerformance = measurePerformance {
      for i in 0 ..< iterations {
        coreAnimationView.currentProgress = Double(i) / Double(iterations)
      }
    }

    print("Core Animation engine took \(coreAnimationEnginePerformance) seconds")

    let ratio = coreAnimationEnginePerformance / mainThreadEnginePerformance
    print("Core Animation engine is \(ratio)x slower than the Main Thread engine")
    return ratio
  }

  @discardableResult
  private func setupAnimationView(with animation: Animation, configuration: LottieConfiguration) -> AnimationView {
    let animationView = AnimationView(animation: animation, configuration: configuration)
    animationView.frame.size = CGSize(width: animation.width, height: animation.height)
    animationView.layoutIfNeeded()
    animationView.currentProgress = 0.5
    return animationView
  }

  private func measurePerformance(_ block : () -> Void) -> TimeInterval {
    let start = DispatchTime.now()
    block()
    let end = DispatchTime.now()
    let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
    return Double(nanoTime) / 1_000_000_000
  }

}


Review comment (suggestion):
These logs presupposes a ratio difference, perhaps we could use something like
```suggestion
    print("Core Animation engine took \(ratio)x as long as the Main Thread engine")
```

Code after:
    print("Main thread engine took \(mainThreadEnginePerformance) seconds")

    let coreAnimationEnginePerformance = measurePerformance {
      for _ in 0..<iterations {
        CATransaction.begin()
        setupAnimationView(with: animation, configuration: .init(renderingEngine: .coreAnimation))
        CATransaction.commit()
      }
    }

    print("Core Animation engine took \(coreAnimationEnginePerformance) seconds")

    let ratio = coreAnimationEnginePerformance / mainThreadEnginePerformance
    print("Core Animation engine took \(ratio)x as long as the Main Thread engine")
    return ratio
  }

  /// Compares performance of scrubbing the given animation with both the Main Thread and Core Animation engine,
  /// and returns the ratio of how much slower the Core Animation is than the Main Thread engine
  private func compareEngineScrubbingPerformance(for animation: Animation, iterations: Int) -> Double {
    let mainThreadAnimationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: .mainThread))
    let mainThreadEnginePerformance = measurePerformance {
      for i in 0..<iterations {
        mainThreadAnimationView.currentProgress = Double(i) / Double(iterations)

        // Since the main thread engine only re-renders in `display()`, which is normally called by Core Animation,
        // we have to invoke it manually. This triggers the same code path that would happen when scrubbing in
        // a real use case.
        mainThreadAnimationView.animationLayer!.display()
      }
    }

    print("Main thread engine took \(mainThreadEnginePerformance) seconds")

    let coreAnimationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: .coreAnimation))
    let coreAnimationEnginePerformance = measurePerformance {
      for i in 0..<iterations {
        coreAnimationView.currentProgress = Double(i) / Double(iterations)
      }
    }

    print("Core Animation engine took \(coreAnimationEnginePerformance) seconds")

    let ratio = coreAnimationEnginePerformance / mainThreadEnginePerformance
    print("Core Animation engine took \(ratio)x as long as the Main Thread engine")
    return ratio
  }

  @discardableResult
  private func setupAnimationView(with animation: Animation, configuration: LottieConfiguration) -> AnimationView {
    let animationView = AnimationView(animation: animation, configuration: configuration)