Я хотел протестировать плагины go, чтобы увидеть разницу в производительности. Поэтому я сделал файл main.go со следующим кодом:
package main
import (
"math/rand"
"strings"
)
// RandString generates and returns a random 50 character string
func RandString(n int) string {
rand.Seed(int64(n))
chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz" +
"0123456789")
var b strings.Builder
for i := 0; i < 50; i++ {
b.WriteRune(chars[rand.Intn(len(chars))])
}
return b.String()
}
Затем я превращаю это в файл plugin.so.
go build -buildmode=plugin -o plugin.so main.go
Затем я написал две контрольные функции, чтобы проверить производительность встроенной функции по сравнению с запуском ее через плагин go.
// BenchmarkRandString tests generating a random string without a go plugin
func BenchmarkRandString(b *testing.B) {
for i := 0; i < b.N; i++ {
RandString(rand.Int())
}
}
// BenchmarkPluginRandString tests generating a random string with a go plugin
func BenchmarkPluginRandString(b *testing.B) {
plug, err := plugin.Open("./plugin.so")
if err != nil {
panic(err)
}
randString, err := plug.Lookup("RandString")
if err != nil {
panic(err)
}
randFunc, ok := randString.(func(n int) string)
if !ok {
panic("unexpected type from module symbol")
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randFunc(rand.Int())
}
}
Как и следовало ожидать, плагин немного медленнее, но ненамного.
BenchmarkRandString-12 128064 8600 нс/оп
BenchmarkPluginRandString-12 132007 8713 нс/оп
Затем я хотел добавить еще 2 бенчмарка, поэтому я добавил еще одну функцию для генерации случайного целого числа и собрал плагин так же, как и раньше.
// RandInt uses math/rand to return a random integer
func RandInt() int {
return rand.Int()
}
Затем мои новые контрольные функции были добавлены над предыдущими двумя контрольными функциями.
// BenchmarkRandInt tests math/rand for generating random integers without a go plugin
func BenchmarkRandInt(b *testing.B) {
for i := 0; i < b.N; i++ {
RandInt()
}
}
// BenchmarkPluginRandInt uses a go plugin and tests math/rand for generating random integers
func BenchmarkPluginRandInt(b *testing.B) {
plug, err := plugin.Open("./plugin.so")
if err != nil {
panic(err)
}
randInt, err := plug.Lookup("RandInt")
if err != nil {
panic(err)
}
randFunc, ok := randInt.(func() int)
if !ok {
panic("unexpected type from module symbol")
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randFunc()
}
}
Теперь, когда я снова запускаю тест, я получаю следующий результат:
BenchmarkRandInt-12 77320668 13,2 нс/оп
BenchmarkPluginRandInt-12 76371756 13,9 нс/оп
BenchmarkRandString-12 136243 8600 нс/оп
BenchmarkPluginRandString-12 142112 8564 нс/оп
Я могу запускать тест снова и снова, и BenchmarkRandString-12 всегда немного медленнее, чем BenchmarkPluginRandString-12, чего я не ожидал. Почему функция плагина go работает немного быстрее при таком бенчмаркинге?
У меня есть проект Github со всем исходным кодом, который я использую здесь: https://github.com/uberswe/goplugins/tree/4825172e011da9578553d113bac7933ca9ecd038
Что может быть медленнее с функцией «плагина», так это ее загрузка и утверждение типа. Как только вы это сделаете, не должно быть снижения производительности по сравнению с функцией, определенной в вашем приложении.
Такие небольшие отклонения могут быть результатом внутреннего управления памятью Go и сборки мусора. Например, если в вашем файле main_test.go
я перемещаю BenchmarkPluginRandString()
выше BenchmarkRandString()
, то результат теста будет «обратным»: BenchmarkRandString()
становится немного медленнее.
Чтобы избавиться от таких недетерминированных факторов, вы можете попробовать запустить тесты изолированно, например. запускать только по одному с
go test -bench BenchmarkRandString
и
go test -bench BenchmarkPluginRandString
И сделать это несколько раз, и вычислить среднее значение. Таким образом, нет заметной разницы.