Mesure du temps d'exécution dans un programme Go

Une opération courante lors du développement d'une application Go, ou de son analyse comparative pour trouver des moyens de l'améliorer, consiste à comparer le temps d'exécution d'une fonction. J'ai introduit le sujet deProfilage du processeur et de la mémoire dans Godéjà, mais cela est différent et plus adapté à la mesure du temps d'exécution de fonction ad-hoc.

Déterminez l'intervalle de temps entre deux expressions: utiliseztime.Since

time.Sinceest une fonction fournie par letimepackage de bibliothèque standard qui prend unTimevalue, calcule la différence avec l'heure actuelle et renvoie unDurationvalue, qui est un type int64 avec unString()ci-joint, qui fournit une notation humaine pour le temps calculé.

import (
    "fmt"
    "time"
)

func main() { start := time.Now()

<span style="color:#75715e">//... do something

fmt.Println(time.Since(start)) }

Intérieurement,time.Sinceest un raccourci pourtime.Now().Sub(t).

Utilisantdeferpour mesurer le temps à l'intérieur d'une fonction

Vous pouvez résumer cela en utilisantdefer:

package main

import ( “log” “time” )

func runningtime(s string) (string, time.Time) { log.Println("Start: ", s) return s, time.Now() }

func track(s string, startTime time.Time) { endTime := time.Now() log.Println("End: ", s, “took”, endTime.Sub(startTime)) }

func execute() { defer track(runningtime(“execute”)) time.Sleep(3 * time.Second) }

func main() { execute() }

Benchmarkez les performances d'une fonction en l'exécutant plusieurs fois: utilisez letestingpaquet

Voici une bibliothèque simple que j'ai écrite, qui compte le nombre de runes dans une chaîne.

runecount.go

package runecount

import “unicode/utf8”

func RuneCount(s string) int { return len([]rune(s)) }

func RuneCount2(s string) int { return utf8.RuneCountInString(s) }

Laquelle des deux façons de compter les runes dans une chaîne est la plus rapide? Nous le découvrons en écrivant un benchmark. Les benchmarks vivent dans le*_test.gofichier, comme les tests réguliers, et peut rester en direct avec des méthodes de test, qui commencent parTest*, mais ils commencent parBenchmark*au lieu:

runecount_test.go

package runecount

import “testing”

func BenchmarkRuneCount(b testing.B) { s := “Gophers are amazing 😁” for i := 0; i < b.N; i++ { RuneCount(s) } } func BenchmarkRuneCount2(b testing.B) { s := “Gophers are amazing 😁” for i := 0; i < b.N; i++ { RuneCount2(s) } }

Je lance cela en exécutant, dans le même dossier où se trouvent les fichiers:

go test -bench=.

Cela provoque l'exécution de tous les benchmarks. Je n'ai qu'un seul fichier, mais au cas où vous en auriez plusieurs, vous pouvez spécifier le fichier à exécuter.

Chaque benchmark exécute la boucle en passant la variable bN, qui est automatiquement déterminée par le benchmark runner, jusqu'à ce que la moyenne soit suffisamment stable pour déterminer un résultat.

Voici la sortie que j'obtiens sur mon Mac:

# flavio @ Flavios-MacBook-Pro in ~/go/src/github.com/flaviocopes/snippets/benchmark on git:master x [18:32:26]
$ go test -bench=.
BenchmarkRuneCount-2            20000000               115 ns/op
BenchmarkRuneCount2-2           30000000                44.1 ns/op
PASS
ok      github.com/flaviocopes/snippets/benchmark       3.812s

20000000et30000000sont le nombre d'opérations exécutées.

La commande renvoie les résultats du benchmark: elle a été exécutée 20 millions de foisRuneCount(), qui a pris en moyenne 163 nanosecondes, puis 30 millions de foisRuneCount2(), ce qui a pris en moyenne 74,2 nanosecondes par itération.

AlorsBenchmark1, qui courtRuneCount(), est 2x plus lent queRuneCount2(), qui utilise la fonction intégréeutf8.RuneCountInStringfonction. Pas de surprise ici, carutf8.RuneCountInStringest hautement optimisé pour cette tâche spécifique.


À la place d'utilisergo test, vous pouvez également appelertesting.Benchmarkà partir d'une commande:

package main

import ( “fmt” “testing” “runecount” )

func BenchmarkRuneCount(b testing.B) { s := “Gophers are amazing 😁” for i := 0; i < b.N; i++ { RuneCount(s) } } func BenchmarkRuneCount2(b testing.B) { s := “Gophers are amazing 😁” for i := 0; i < b.N; i++ { RuneCount2(s) } }

func main() { fmt.Println(testing.Benchmark(BenchmarkRuneCount)) fmt.Println(testing.Benchmark(BenchmarkRuneCount2)) }

Où en savoir plus


Plus de tutoriels go: