Почему вызов функции через плагин go выполняется быстрее, чем вызов функции напрямую

Я хотел протестировать плагины 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

Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
1
0
113
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Что может быть медленнее с функцией «плагина», так это ее загрузка и утверждение типа. Как только вы это сделаете, не должно быть снижения производительности по сравнению с функцией, определенной в вашем приложении.

Такие небольшие отклонения могут быть результатом внутреннего управления памятью Go и сборки мусора. Например, если в вашем файле main_test.go я перемещаю BenchmarkPluginRandString() выше BenchmarkRandString(), то результат теста будет «обратным»: BenchmarkRandString() становится немного медленнее.

Чтобы избавиться от таких недетерминированных факторов, вы можете попробовать запустить тесты изолированно, например. запускать только по одному с

go test -bench BenchmarkRandString

и

go test -bench BenchmarkPluginRandString

И сделать это несколько раз, и вычислить среднее значение. Таким образом, нет заметной разницы.

Другие вопросы по теме