How to Benchmark Swift Code Execution

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



Leave a reply



Submit