When writing code I've frequently used a technique for poor mans profiling where you start a timer, perform a set of operations and then see how much time has elapsed. This technique is not a substitute for real profiling tools like valgrind, but it sometimes is handy and sufficient. In C++ or Java I'd create a Timer class that will get the current time when it's constructed and then add an elapsed() method which will return how many nano/milli seconds have passed since the object was constructed. I needed this for a Go project I'm working on and this is how I did it:
First of all I define a new type.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A simple timer | |
type Timer time.Time |
Now that we have a new type lets create a function for creating a new Timer. In Java we'd probably call this method timerFactory, but will just call it StartTimer().
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Create and start a new timer | |
func StartTimer() Timer { | |
return Timer(time.Now()) | |
} |
Now we want to add a method to our Timer object that will return the number of nanoseconds that have elapsed since the Timer was created.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Return the number of nanoseconds that have elapsed. | |
func (t Timer) ElapsedNanoseconds() int64 { | |
return time.Now().Sub(time.Time(t)).Nanoseconds() | |
} |
We can now use our new Timer type as follows:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
timer := StartTimer() | |
time.Sleep(100000000) | |
print(timer.ElapsesNanoseconds(), "\n") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Return the number of milliseconds that have elapsed. | |
func (t Timer) ElapsedMilliseconds() int64 { | |
return t.ElapsedNanoseconds() / 1000000 | |
} |
While this code works perfectly fine, the code will make it easy for us to write a bug. Consider the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
timer0 := StartTimer() | |
time.Sleep(1000000000) // Sleep for one second | |
shortDelay := timer0.ElapsedNanoseconds() | |
timer1 := StartTimer() | |
time.Sleep(2 * 1000000000) // Sleep for two seconds | |
longDelay := timer1.ElapsedMilliseconds() | |
if shortDelay > longDelay { | |
fmt.Print("Something bad happened\n") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A simple timer for seeing how long an operation lasts | |
type Timer time.Time | |
// Create and start a new timer | |
func StartTimer() Timer { | |
return Timer(time.Now()) | |
} | |
// A Nanosecond time value | |
type Nanosecond int64 | |
// Return the number of nanoseconds that have elapsed. | |
func (t Timer) ElapsedNanoseconds() Nanosecond { | |
return Nanosecond(time.Now().Sub(time.Time(t)).Nanoseconds()) | |
} | |
// A Millisecond time value | |
type Millisecond int64 | |
// Return the number of milliseconds that have elapsed. | |
func (t Timer) ElapsedMilliseconds() Millisecond { | |
return Millisecond(t.ElapsedNanoseconds() / 1000000) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Convert Nanoseconds to Milliseconds | |
func (n Nanosecond) ToMilliseconds() Millisecond { | |
return Millisecond(n / 1000000) | |
} | |
// Convert Milliseconds to Nanoseconds | |
func (m Millisecond) ToNanoseconds() Nanosecond { | |
return Nanosecond(m * 1000000) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
timer0 := StartTimer() | |
time.Sleep(1000000000) // Sleep for one second | |
shortDelay := timer0.ElapsedNanoseconds() | |
timer1 := StartTimer() | |
time.Sleep(2 * 1000000000) // Sleep for two seconds | |
longDelay := timer1.ElapsedMilliseconds() | |
if shortDelay.ToMilliseconds() > longDelay { | |
fmt.Print("Something bad happened\n") | |
} |
That's it! We created three new types Timer, Millisecond, Nanosecond (they would be classes in C++/Java) and one factory function and three new methods. The full code with the example usage can be downloaded and executed by typing "go run main.go".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"time" | |
) | |
// A simple timer | |
type Timer time.Time | |
// Create and start a new timer | |
func StartTimer() Timer { | |
return Timer(time.Now()) | |
} | |
// A Nanosecond time value | |
type Nanosecond int64 | |
// Return the number of nanoseconds that have elapsed. | |
func (t Timer) ElapsedNanoseconds() Nanosecond { | |
return Nanosecond(time.Now().Sub(time.Time(t)).Nanoseconds()) | |
} | |
// A Millisecond time value | |
type Millisecond int64 | |
// Return the number of milliseconds that have elapsed. | |
func (t Timer) ElapsedMilliseconds() Millisecond { | |
return Millisecond(t.ElapsedNanoseconds() / 1000000) | |
} | |
// Convert Nanoseconds to Milliseconds | |
func (n Nanosecond) ToMilliseconds() Millisecond { | |
return Millisecond(n / 1000000) | |
} | |
// Convert Milliseconds to Nanoseconds | |
func (m Millisecond) ToNanoseconds() Nanosecond { | |
return Nanosecond(m * 1000000) | |
} | |
func main() { | |
timer0 := StartTimer() | |
time.Sleep(1000000000) // Sleep for one second | |
shortDelay := timer0.ElapsedNanoseconds() | |
timer1 := StartTimer() | |
time.Sleep(2 * 1000000000) // Sleep for two seconds | |
longDelay := timer1.ElapsedMilliseconds() | |
if shortDelay.ToMilliseconds() > longDelay { | |
fmt.Print("Something bad happened\n") | |
} | |
fmt.Print("Bubye!\n") | |
} |