Tim
Tim

Reputation: 1894

Why `String(describing: Class.self)` is slower than `NSStringFromClass(Class.self)`?

Consider the following programm:

import Foundation
import QuartzCore

class C: NSObject {}

func benchmark() {
    let swift1 = CACurrentMediaTime()
    let swiftString = String(describing: C.self)
    let swift2 = CACurrentMediaTime()

    let objc1 = CACurrentMediaTime()
    let objcString = NSStringFromClass(C.self)
    let objc2 = CACurrentMediaTime()

    let timeForSwiftString = swift2 - swift1
    let timeForObjcString = objc2 - objc1
    print("timeForSwiftString / timeForObjcString:", timeForSwiftString / timeForObjcString)
}

for _ in 0..<20 {
    benchmark()
}

The output will look something like

timeForSwiftString / timeForObjcString: 21.023079257409023
timeForSwiftString / timeForObjcString: 1.8032004909527086
timeForSwiftString / timeForObjcString: 2.929076507541501
timeForSwiftString / timeForObjcString: 3.1707763580662034
timeForSwiftString / timeForObjcString: 2.884789101770234
timeForSwiftString / timeForObjcString: 3.167761588431754
timeForSwiftString / timeForObjcString: 3.0066055331053776
timeForSwiftString / timeForObjcString: 3.0707174733951255
timeForSwiftString / timeForObjcString: 3.213085166384659
timeForSwiftString / timeForObjcString: 2.830997374357457
timeForSwiftString / timeForObjcString: 3.24157236450268
timeForSwiftString / timeForObjcString: 3.0462955824531646
timeForSwiftString / timeForObjcString: 2.887874789238326
timeForSwiftString / timeForObjcString: 3.1071486398963732
timeForSwiftString / timeForObjcString: 2.995876045716979
timeForSwiftString / timeForObjcString: 3.073833893272912
timeForSwiftString / timeForObjcString: 2.965068621921414
timeForSwiftString / timeForObjcString: 3.0622197750334306
timeForSwiftString / timeForObjcString: 3.0484172031541656
timeForSwiftString / timeForObjcString: 1.9362930766842597

The first measurement is always the most expensive (perhaps due to some lazy runtime actions), so we will skip it.

All other measurements show, that swift is slower.

I also have a demo project with a benchmark on getting string from 1000 different classes which shows:

Any ideas for such a behavior?

Upvotes: 3

Views: 1996

Answers (2)

matt
matt

Reputation: 534977

You’re comparing apples with motorcars.

  • NSStringFromClass does a highly specific job, involving only class types, and is used for a conversion related to the dynamic nature of Objective-C programs.

  • String(describing:) in Swift turns anything into a string representation and is intended (and suitable) only for debugging. It will not be used for any user-facing code and no meaningful program action will depend upon its output. Thus it will not appear in a released app and its speed is of no account. Your question therefore optimizes not only prematurely but unnecessarily; the speed of String(describing:) is unimportant.

Upvotes: 4

CRD
CRD

Reputation: 53000

perhaps due to some lazy runtime actions

Spot on, first calls to anything can be slower for various reasons including resolution of dynamically loaded libraries.

Any ideas for such a behavior?

A first guess would be that String(describing:) takes any value, figures out what kind of value it is (it's description states it tests for conformance to three different protocols), and then calls the appropriate functions to describe the value. However NSStringFromClass takes only Class values, which is checked at compile time, and then directly calls the runtime functions needed to get the class name as a string without any need for testing and branching on the kind of value passed.

TLDR: More work takes more time, testing and branching slows you down.

HTH

Upvotes: 3

Related Questions