Home Swift UNIX C Assembly Go Web MCU Research Non-Tech

How to measure running time of function in Swift/SwiftUI

2023-04-02 | Swift | #Words: 973 | 中文原版

Sometimes, Xcode Test cannot meet our need, so we need to write the testing function mannully. The main of testing function is measure time of code. Whether it is testing a CPU/GPU-intensive program or a read-write-intensive program, it is necessary to obtain the running time of the program or function to calculate performance.

If you measure the CLI (Command Line Interface) program, you can get the running time of the entire program by time command. But if you want to test the running time of a function in the program code, or test a GUI program, this method will not work. We need to add a “timer” to the code to schedule the measurement work mannully.

There are two ways to implement this measurement in Swift. The first is more convenient but only can use on new systems. The second is more compatible but unsafe (the accuracy is slightly worse, but it can be ignored).

Simple method on new systems: measure

After iOS 16 and macOS 13, Apple added new data structures Clock and ContinuousClock, both of which have a method measure, which can be used to test the running time of a function.

measure document

It’s also very simple to use.

First, create a instance ContinuousClock:

let clock = ContinuousClock()

Then use the following statement to get the running time of a function:

let elapsed = clock.measure {
	someWork()
}

Notice, let is mandatory. You cannot declare a variable before and then instantiate it here.

The elapsed here is of type ContinuousClock.Instant.Duration and needs to be converted into a string for use. The conversion method is as follows:

var duration: String = ""
let elapsed = clock.measure {
	quicksort()
}
//elapsed.description is String
duration = elapsed.description

The entire code is:

struct ContentView: View {
    var clock = ContinuousClock()
    @State private var duration: String = ""
    var body: some View {
        VStack {
            // Press button to quicksort
            Text("点击按钮进行快速排序测试:")
                .padding()
                
            Button(action: {
                // Get time interval, it is running time
                let elapsed = clock.measure {
                    // test function is quicksort
                	quicksort()
                }
                // Convert time interval to String
                duration = elapsed.description
            }) {
                Text("Start")
            }
            .padding()

			// Display running time. Because elapsed.description end with string "second", din't add any suffix
            Text("消耗时间为:\(duration)")
                .padding()
        }
        .padding()
    }
}

Here’s what it looks like when running in the app:

what it looks like when running in the app

General method

If it is not the latest system or you want to write related method functions by yourself, you can use the classic method: time difference. Record the start time and end time, and then make subtraction to get the running time.

So what is this time based on? The C language can use clock time to measure single thread function, but Swift only has clock time starting from iOS 16. Before that, only have Date type. Fortunately, Date can achieve our purpose, just use the timeIntervalSince1970 value.

The value timeIntervalSince1970 is a historical value. All systems (not just Apple systems) have this value, because 1970 is the starting time of Unix, or call “Unix Time”, and timeIntervalSince1970 is recorded time from 1970-01-01 00:00:00 to this moment. The accuracy can reach the decimal point to milliseconds (or even smaller, and the accuracy is similar to the previous method).

Since only one part is different from the previous method, the entire code is listed directly:

struct ContentView: View {
    @State private var duration: String = ""
    var body: some View {
        VStack {
            Text("点击按钮进行快速排序测试:")
                .padding()
            Button(action: {
            	// Record start time
                let start_time = Date().timeIntervalSince1970
                // run function measured
                quicksort()
                // Record end time
                let end_time = Date().timeIntervalSince1970
                //make subtraction to get the running time, and convert to String
                duration = TimeInterval(end_time-start_time).description
            }) {
                Text("Start")
            }
            .padding()

			// Because no suffix, so we add "seconds" here
            Text("消耗时间为:\(duration) seconds")
                .padding()
        }
        .padding()
    }
}

The running effect is same as the previous method, so I don’t show here.

I hope these will help someone in need~