How to benchmark Swift code execution?
If you just want a standalone timing function for a block of code, I use the following Swift helper functions:
func printTimeElapsedWhenRunningCode(title:String, operation:()->()) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
func timeElapsedInSecondsWhenRunningCode(operation: ()->()) -> Double {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
return Double(timeElapsed)
}
The former will log out the time required for a given section of code, with the latter returning that as a float. As an example of the first variant:
printTimeElapsedWhenRunningCode(title:"map()") {
let resultArray1 = randoms.map { pow(sin(CGFloat($0)), 10.0) }
}
will log out something like:
Time elapsed for map(): 0.0617449879646301 s
Be aware that Swift benchmarks will vary heavily based on the level of optimization you select, so this may only be useful for relative comparisons of Swift execution time. Even that may change on a per-beta-version basis.
Find how long it takes a function to execute in seconds
Try this:
let start = Date.timeIntervalSinceReferenceDate
// do stuff
let end = Date.timeIntervalSinceReferenceDate
let secondsElapsed = end - start
secondsElapsed
will be a Double
, but it will be in seconds. You can round it or truncate it if you want an Int
.
Hope this helps!
How to measure code block execution time inside DispatchQueue concurrent async in Swift?
CACurrentMediaTime
is a fine way of measuring time. It’s low overhead and doesn’t suffer the problems of Date
or CFAbsoluteTimeGetCurrent
, which “do not guarantee monotonically increasing results.”
A few general guidelines when benchmarking include:
- let the app reach quiescence before starting the benchmarking;
- do not run independent tests concurrently with respect to each other (because they may be contending for the same resources);
- repeat it many times; and
- change the order that the benchmarks are done in order to eliminate noise from the measurements.
If you’re looking for an alternative to manually using CACurrentMediaTime
, another approach is to use signposts and points of interest, which not only will capture the amount of time it takes, but would you let you filter your instruments timeline to the relevant period of time (and you might be able to diagnose the source of the discrepancy).
Measure elapsed time in Swift
Here's a Swift function I wrote to measure Project Euler problems in Swift
As of Swift 3, there is now a version of Grand Central Dispatch that is "swiftified". So the correct answer is probably to use the DispatchTime API.
My function would look something like:
// Swift 3
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
print("Evaluating problem \(problemNumber)")
let start = DispatchTime.now() // <<< Start time
let myGuess = problemBlock()
let end = DispatchTime.now() // <<< end time
let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<< Difference in nano seconds (UInt64)
let timeInterval = Double(nanoTime) / 1_000_000_000 // Technically could overflow for long running tests
print("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
return theAnswer
}
Old answer
For Swift 1 and 2, my function uses NSDate:
// Swift 1
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
println("Evaluating problem \(problemNumber)")
let start = NSDate() // <<< Start time
let myGuess = problemBlock()
let end = NSDate() // <<< end time
let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)
let timeInterval: Double = end.timeIntervalSinceDate(start) // <<< Difference in seconds (double)
println("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
return theAnswer
}
Note that using NSdate for timing functions is discouraged: "The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.".
How to log a method's execution time exactly in milliseconds?
NSDate *methodStart = [NSDate date];
/* ... Do whatever you need to do ... */
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);
Swift:
let methodStart = NSDate()
/* ... Do whatever you need to do ... */
let methodFinish = NSDate()
let executionTime = methodFinish.timeIntervalSinceDate(methodStart)
print("Execution time: \(executionTime)")
Swift3:
let methodStart = Date()
/* ... Do whatever you need to do ... */
let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
Easy to use and has sub-millisecond precision.
swift LLDB produces correct results, but different results from code execution
So here is the offending code.
currIndexPlaceA = a!.startIndex.successor()..<newText.endIndex;
currIndexPlaceB = b!.startIndex.successor()..<newText.endIndex;
var a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
A definite bug is the shadowing of a
and b
declared outside the while
loop with the var a
and var b
in this else
block. As for your LLDB problem, make absolutely sure that you have stopped on the line where currIndexPlaceB
is set and not on or after where var a
is set. Make sure all optimizations have been turned off as that may be causing code placement to be shuffled.
(Question writer... Adding code edits which solved the issue)
I'm adding here the final code, with additional bugs fixed (so if someone wants to get tags from xml - you'll have a copy-paste).
When debugging initially - I did't understand what was going on so I started using endIndex
instead of startIndex
etc.
func getTagContent(text : String, tagName : String) -> [String!]!{
// Extracts reply
var newText = text;
var currProcessing = text;
var retVal = [String!]();
while (newText != ""){
let rangeFirst = newText.rangeOfString("<" + tagName, options: [], range: nil, locale: nil);
if let actrange = rangeFirst
{
newText = newText.substringFromIndex(actrange.startIndex.advancedBy(tagName.characters.count + 1));
let rangeEndFirstTag = newText.rangeOfString(">");
if let actRangeEndFirstTab = rangeEndFirstTag {
let distToCloseTag = newText.startIndex.distanceTo(actRangeEndFirstTab.startIndex)
if (distToCloseTag == 0 || (distToCloseTag > 0 && newText[actRangeEndFirstTab.startIndex.predecessor()] != "/")) {
newText = newText.substringFromIndex(actRangeEndFirstTab.startIndex.advancedBy(1));
var currIndexPlaceA = newText.startIndex..<newText.endIndex;
var currIndexPlaceB = newText.startIndex..<newText.endIndex;
var a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
if a == nil {a = newText.rangeOfString("</" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);}
var b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
if b == nil {b = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);}
while (a != nil && b != nil && b?.startIndex < a?.startIndex){
let txt = newText.substringFromIndex(b!.startIndex);
var closedTag = false;
if let tmpRagne = txt.rangeOfString(">"){
if (tmpRagne.startIndex > txt.startIndex){
if (txt[tmpRagne.startIndex.predecessor()] == "/"){
closedTag = true;
}
}
}
if closedTag{
currIndexPlaceB = b!.endIndex..<newText.endIndex;
b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
let tmpb = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
if (b == nil || (tmpb != nil && tmpb?.startIndex < b?.startIndex)) {b = tmpb;}
}
else{
currIndexPlaceA = a!.endIndex..<newText.endIndex;
currIndexPlaceB = b!.endIndex..<newText.endIndex;
a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
let tmpa = newText.rangeOfString("</" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
if (a == nil || (tmpa != nil && tmpa!.startIndex < a?.startIndex)) {a = tmpa;}
b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
let tmpb = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
if (b == nil || (tmpb != nil && tmpb?.startIndex < b?.startIndex)) {b = tmpb;}
}
}
if a == nil { return nil; }
currProcessing = newText.substringToIndex(a!.startIndex);
retVal.append(currProcessing);
newText = newText.substringFromIndex(a!.startIndex.successor());
if let lastRange = newText.rangeOfString(">"){
newText = newText.substringFromIndex(lastRange.endIndex);
}
else {
return nil;
}
}
else{
newText = newText.substringFromIndex(actRangeEndFirstTab.startIndex.successor());
retVal.append(nil);
}
}
}
else {
break;
}
}
return retVal;
}
Related Topics
How to Create a Noop Block for a Switch Case in Swift
How to Convert Int32 to Int in Swift
Use 'Self' as a Default Parameter
How to Get Next Case of Enum(I.E. Write a Circulating Method) in Swift 4.2
iOS 11 Uirefreshcontrol with Navigationbar Largetitle and Searchcontroller Disappearing
How to Convert Nsnull to Nil in Swift
Convert Emoji to Hex Value Using Swift
Swift Uipasteboard Not Copying Png
!? Strange Double Unwrapped Optional Syntax in For_In []
How to Apply Borders and Corner Radius to Uibarbuttonitem
How to Install the Alamofire 4.0 in Xcode 8.0
Filter by Multiple Array Conditions
Swift Codable Decode Manually Optional Variable
How to Hide the Navigation Back Button in Swiftui
Rxswift/Rxcocoa: Prevent Uitextfield from Having More Than ... Characters