From c7f6ad9d4a4c37418bede71b97e71aed974be888 Mon Sep 17 00:00:00 2001 From: Andrew Benson Date: Wed, 8 Nov 2023 10:46:37 -0600 Subject: [PATCH] Improves clean Debug build time by about 30%. Resolves issue #5123 "Charts framework takes a long time to build" (https://github.com/danielgindi/Charts/issues/5123) by implementing the recommendations listed at the end of the issue: - Set `Eager Linking` build setting to `Yes` for `Charts` framework target - Set `Enable Module Verifier` build setting to `No` for `Debug` builds - Set `Compilation Mode` to `Incremental` for `Debug` builds - Add `-Xfrontend -warn-long-expression-type-checking=50` (or perhaps use `100`) to `OTHER_SWIFT_FLAGS` so the Swift compiler will emit warnings for expressions that take a long time to type-check. The number after the = is the number of milliseconds threshold, above which warnings will be emitted. - Refactored the slow type-checking hotspots identified above in `ChartAnimationEasing.swift`, breaking out expressions which are slow to type-check into separate, quicker-to-type-check expressions. --- Charts.xcodeproj/project.pbxproj | 9 ++++-- .../Animation/ChartAnimationEasing.swift | 30 +++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Charts.xcodeproj/project.pbxproj b/Charts.xcodeproj/project.pbxproj index 4f4bd31bf6..03114f2463 100644 --- a/Charts.xcodeproj/project.pbxproj +++ b/Charts.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 53; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -1034,7 +1034,8 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; + EAGER_LINKING = YES; + ENABLE_MODULE_VERIFIER = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_VERSION = A; GCC_NO_COMMON_BLOCKS = YES; @@ -1051,12 +1052,14 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; MTL_ENABLE_DEBUG_INFO = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=50"; PRODUCT_BUNDLE_IDENTIFIER = com.dcg.Charts; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 12.0; @@ -1227,6 +1230,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + EAGER_LINKING = YES; ENABLE_MODULE_VERIFIER = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_VERSION = A; @@ -1244,6 +1248,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; MTL_ENABLE_DEBUG_INFO = NO; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=50"; PRODUCT_BUNDLE_IDENTIFIER = com.dcg.Charts; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; diff --git a/Source/Charts/Animation/ChartAnimationEasing.swift b/Source/Charts/Animation/ChartAnimationEasing.swift index a6453d9e0c..a289338603 100644 --- a/Source/Charts/Animation/ChartAnimationEasing.swift +++ b/Source/Charts/Animation/ChartAnimationEasing.swift @@ -249,7 +249,12 @@ internal struct EasingFunctions } position = position - 1.0 - return Double( 0.5 * (-pow(2.0, -10.0 * position) + 2.0) ) + + // compute partial result so Xcode's type-checker doesn't take too long + let partialResult: Double = -pow(2.0, -10.0 * position) + 2.0 + + // took 120ms to type check before breaking out partial result, above + return Double( 0.5 * partialResult ) } internal static let EaseInCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in @@ -265,12 +270,17 @@ internal struct EasingFunctions internal static let EaseInOutCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position: TimeInterval = elapsed / (duration / 2.0) + + // calculate partial result so Swift compiler doesn't lose its mind + let sqrtPartialResult: Double = sqrt(1.0 - position * position) if position < 1.0 { - return Double( -0.5 * (sqrt(1.0 - position * position) - 1.0) ) + // was 800ms to type check with inlined sqrt calculation, from above + return Double( -0.5 * (sqrtPartialResult - 1.0) ) } position -= 2.0 - return Double( 0.5 * (sqrt(1.0 - position * position) + 1.0) ) + // was 1500ms to type check with inlined sqrt calculation, from above + return Double( 0.5 * (sqrtPartialResult + 1.0) ) } internal static let EaseInElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in @@ -328,13 +338,23 @@ internal struct EasingFunctions return Double( -0.5 * (pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p)) ) } position -= 1.0 - return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p) * 0.5 + 1.0 ) + + // Break out partial result so the Swift compiler doesn't lose its mind + let sinPartialResult: Double = sin((position * duration - s) * (2.0 * Double.pi) / p) + + // Original expression here, with the expression above inlined, took 600ms to type check + return Double( pow(2.0, -10.0 * position) * sinPartialResult * 0.5 + 1.0 ) } internal static let EaseInBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in let s: TimeInterval = 1.70158 var position: TimeInterval = elapsed / duration - return Double( position * position * ((s + 1.0) * position - s) ) + + // Break out partial result so the Swift compiler doesn't lose its mind + let partialResult: Double = ((s + 1.0) * position - s) + + // Original expression here, with partialResult inlined, took 260ms to type check + return Double( position * position * partialResult ) } internal static let EaseOutBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in