Библиотеки шаблонов Go: Сравнение производительности

Автор Аншул Гоял✏️

Библиотеки шаблонов являются важной частью экосистемы языка программирования. Стандартная библиотека Go предоставляет мощные библиотеки шаблонов из коробки, но в Go также есть множество библиотек шаблонов, созданных сообществом, которые могут использовать разработчики.

При наличии нескольких вариантов может быть трудно выбрать лучший вариант для конкретного случая использования.

В этой статье мы сравним несколько наиболее популярных библиотек шаблонов по производительности, функциональности и безопасности, используя инструмент бенчмаркинга Go. В частности, мы рассмотрим некоторые распространенные операции, такие как условные операции, вложенность шаблонов, циклы и т.д., используемые в шаблонах, и сравним их, чтобы дать вам представление о производительности и объеме памяти каждого шаблонизатора.

Я решил использовать четыре библиотеки шаблонизаторов Go в качестве основы для этой статьи, основываясь на использовании и применимости для разработчиков сегодня, так как некоторые другие библиотеки либо предварительно скомпилированы, либо поддерживаются редко. Библиотеки, которые мы будем сравнивать сегодня, это template/text, pongo2, liquid и quicktemplate.

Давайте начнем!

  • template/text
    • Производительность
  • pongo2
    • Производительность
  • liquid
    • Производительность
  • quicktemplate

template/text

Стандартная библиотека Go предоставляет полнофункциональный и мощный механизм шаблонизации. Он очень производителен и хорошо оптимизирован.

Производительность

Производительность — самый важный аспект любой компьютерной программы. Стандартная библиотека Go широко используется в производстве и по понятным причинам является популярным вариантом для разработчиков.

Парсинг

Если ваш сценарий использования требует, чтобы шаблоны часто менялись, или вам нужно загружать шаблоны при каждом запросе, то парсинг быстро становится очень важным показателем. Пакет text предоставляет различные методы парсинга; например, парсинг из строки, файла или использование шаблона для чтения файлов из файловой системы.

    // template has loop, if statement function call etc.
    BenchmarkGoParsing-10                      32030             37202 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Вы можете анализировать шаблон при запуске и кэшировать его для повышения производительности. Если ваш шаблон часто меняется, то вы можете использовать cron, который обновляется через фиксированный интервал времени в фоновом режиме.

Подстановка строк

Подстановка строки — это самая основная функция любого шаблонизатора, а text — это супербыстрая подстановка строки.

    // template -> This is a simple string {{ .Name }} with value as string
    BenchmarkGoStd-10                        4185343               288.8 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Подстановка строки происходит быстрее при использовании struct в качестве контекста данных — использование map может быть немного медленным, так как увеличивается время поиска ключа.

    // template -> This is a simple string {{ index . "name" }} with value as string
    BenchmarkGoStdMapWithIndex-10            1333495               852.7 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Как вы видите, использование map является дорогостоящим. Отказ от функции index позволяет оптимизировать приведенный выше шаблон.

    // template -> This is a simple string {{ .Name }} with value as string`
    BenchmarkGoStdMapWith-10                 3563234               338.5 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

. также позволяет нам выполнять поиск ключа по карте.

Условия

Условные элементы являются жизненно важной операцией для шаблонизаторов; неэффективные условные операции приводят к плохой производительности. Пакет text обеспечивает поддержку операций if, с поддержкой операций and и or. Обе эти операции принимают несколько аргументов, где каждый аргумент является типом boolean. text обеспечивает разумную производительность для условных символов.

    // template -> This is the file {{if and .First (eq .Second "value") (ne .Third "value")}}Got{{end}}. Git do
    BenchmarkGoIfString-10                    506409              2323 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Производительность, продемонстрированную выше, можно улучшить, избегая вызовов eq и ne. Каждая функция в template вызывается через отражение, что приводит к снижению производительности.

Если бы eq .Second "value" был в коде, то компилятор смог бы его оптимизировать, так как конкретный тип будет известен при компиляции. Если все вызовы eq и ne заменить простым булевым числом, скорость будет выше.

    // template -> This is the file {{if and .First .Four .Five}}Got{{end}}. Git do
    BenchmarkGoIf-10                          987169              1186 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

(Примечание: Во время бенчмаркинга сравнения строк также включались в расчеты времени)

Циклы

Циклы также предоставляются пакетом text. range используется для итерации по фрагментам, массивам и картам. Производительность циклов зависит от операций, содержащихся в теле цикла.

    // template ->`{{range .Six}}{{.}},{{end}}`
    BenchmarkGoLoop-10                       1283661               919.8 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Аналогично, итерация по карте также отличается высокой производительностью:

    // template -> {{range $key, $value:=.Seven}}{{$key}},{{$value}}:{{end}}
    BenchmarkGoLoopMap-10                     566796              2003 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Вызовы функций

Вызовы функций позволяют разработчику изменять и преобразовывать входные данные внутри шаблона. Но это обычно сопряжено с расходами, поскольку вызовы функций происходят через отражение. Использование функций имеет высокие накладные расходы — если возможно, избегайте вызовов функций.

    // template -> This is a simple string {{ noop }} with value as string
    BenchmarkGoStdCallFunc-10                2611596               472.3 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Вложенные шаблоны

Вложенные шаблоны позволяют пользователям совместно использовать код и избегать ненужного повторения кода. text обеспечивает поддержку вызова вложенных шаблонов.

    // template -> {{define "noop"}} This is {{.Second}} and {{.Third}} {{end}}
    //
    //{{template "noop" .}}

    BenchmarkGoNested-10                     1868461               630.2 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Поддержка сообщества

Стоит отметить, что text, в частности, имеет отличную поддержку сообщества, с такими библиотеками, как sprig, предоставляющими широкий спектр функций, которые могут быть использованы внутри шаблона. Поддержка подсветки синтаксиса и валидации встроена в стандартный пакет шаблонов стандартной библиотеки Go.

pongo2

pongo2 — это созданный сообществом движок шаблонов с синтаксисом, вдохновленным Django-синтаксисом. Он создан сообществом для Go. Сегодня он очень популярен, на GitHub у него более 2 тысяч звезд.

Производительность

pongo2 хорошо оптимизирован для вызовов функций внутри шаблонов. Это полноценный шаблонизатор с циклами, условиями и вложенными шаблонами.

Парсинг

pongo2 поддерживает парсинг шаблонов из строк, байтов, файлов и кэша. Мы видим, что по производительности он немного быстрее, чем пакет шаблонов стандартной библиотеки Go:

    // template has loop, if, nested template and function call
    BenchmarkPongoParsing-10                   38670             29153 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Подстановка строк

Подстановка строк немного медленнее в pongo2 по сравнению с пакетом text. Он поддерживает только map для контекста данных, что приводит к дополнительным операциям поиска, что отражено в следующем примере.

    // template -> This is a simple string {{ name }} with value as string
    BenchmarkPongo2-10                       1815843               654.2 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Условия

pongo2 имеет более удобный для разработчиков синтаксис if/else. Он ближе к тому, как if/else пишутся в языках программирования. С точки зрения производительности if/else в pongo2 более затратен, чем пакет text.

    // template -> Name is {% if First && Four && Five %}got{% endif %}. Go
    BenchmarkPongo2String2If-10               709471              1528 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Циклы

Циклы в pongo2 немного медленнее, чем пакет text:

    // template -> {% for value in Six %} {{ value }}, {% endfor %}
    BenchmarkPongo2String2Loop-10             650672              1796 ns/op
Войти в полноэкранный режим Выход из полноэкранного режима

Петли на карте также медленнее:

    // template -> {% for key,value in Seven %} {{key}},{{ value }}, {% endfor %}
    BenchmarkPongo2String2LoopMap-10          359858              3182 ns/op
Войти в полноэкранный режим Выход из полноэкранного режима

Вызовы функций

Вызовы функций быстрее в pongo2, чем в пакете text, поскольку сигнатура функции известна во время компиляции, и функции не нужно проходить через вызов отражения, что делает ее быстрее.

    // template -> This is a simple string {{ noop }} with value as string
    BenchmarkPongo2StdCallFunc-10            4775058               261.9 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Вложенные шаблоны

Макросы pongo2 предоставляются для работы вложенных шаблонов, которые медленнее, чем пакет text.

    // template -> {% macro noop(first, second) %}
                 This is {{first}} and {{second}}
                 {% endmacro %}
                 {{noop("anshul","goyal")}}

    BenchmarkPongo2String2Nested-10           657597              1665 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

liquid

liquid — это созданная сообществом реализация языка шаблонов Shopify. Он предоставляет полнофункциональную библиотеку шаблонов.

Производительность

По результатам моего исследования liquid достаточно производителен. Это полноценный шаблонизатор с циклами, условиями и вложенными шаблонами.

Парсинг

liquid поддерживает парсинг шаблонов из строк, байтов и файлов. С точки зрения производительности, он немного медленнее, чем пакет шаблонов стандартной библиотеки Go и pongo2.

    // template has loop, if, nested template and function call
    BenchmarkLiquidParsing-10                       29710             40114 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Подстановка строк

Производительность подстановки строк сравнима с pongo2, а liquid немного медленнее, чем пакет text. Он поддерживает только map для контекста данных, что приводит к дополнительным затратам времени на поиск, как показано в следующем примере.

    // template -> This is a simple string {{ name }} with value as string
    BenchmarkLiquidString-10                       1815843                676.0 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Условия

liquid имеет очень дружественный разработчику синтаксис для if/else. Он ближе к тому, как if/else записываются в других известных языках программирования. if/else менее производителен в liquid, чем пакет text, но быстрее, чем pongo2.

    // template -> This is the file {%if First and  Four and Five %}Got{%endif%}. Git do
    BenchmarkLiquidIf-10               709471              953.3 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Циклы

Циклы в liquid также медленнее, чем при использовании пакета text. Циклы медленнее в liquid даже по сравнению с pongo2:

    // template -> {%for value in Six %}{{value}},{%endfor%}
    BenchmarkLiquidLoop-10             650672              3067 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

Вызовы функций

Вызовы функций быстрее в pongo2, чем в liquid, поскольку сигнатура функции извлекается во время компиляции, и функции не нужно проходить через вызов отражения, что делает ее быстрее.

    // template -> This is a simple string {{ noop }} with value as string
    BenchmarkPongo2StdCallFunc-10            4775058               359.0 ns/op
Вход в полноэкранный режим Выход из полноэкранного режима

quicktemplate

quicktemplate — это прекомпилированный шаблон; он преобразует шаблоны в код Go. Он не позволяет разработчикам изменять код во время выполнения. quicktemplate очень быстр, так как не выполняет никаких отражений и все проходит через оптимизатор компилятора.

Если ваш случай использования не требует частых обновлений, то quicktemplate может быть очень хорошим выбором для вас. Ниже приведены контрольные показатели для quicktemplate по сравнению с liquid и fasttemplate (для подстановки строк):

Характеристика Жидкий Fasttemplate Quicktemplate
Парсинг 40114 нс/оп 188,8 нс/оп N/A
Утверждения If 953.3 ns/op Н/О 87,47 нс/оп
Операторы If со строками 1144 нс/оп Н/О 99.18 нс/оп
циклы 3067 нс/оп Н/А 268.8 нс/оп
Функции 359.0 нс/оп Н/Д 191,7 нс/оп
Вложенные шаблоны N/A Н/Д 191.7 ns/op
Подстановка строк 676.0 нс/оп 75,21 нс/оп 105,9 нс/оп

quicktemplate — это предварительно скомпилированный шаблонизатор (т.е. шаблон преобразуется в Go-код). Затем Go-код оптимизируется компилятором, что приводит к очень быстрому выполнению. Он также позволяет избежать отражения, что дает огромный прирост производительности.

(Примечание: quicktemplate обеспечивает быстрое выполнение шаблона, но за счет отсутствия обновлений шаблона во время выполнения. fasttemplate поддерживает только подстановку строк).

Заключение

pongo2 и text имеют свои плюсы и минусы. pongo2 предлагает немного более дружественный для разработчиков синтаксис, чем text, но text обеспечивает лучшую производительность в целом.

Выбор библиотеки шаблонов зависит от того, какая из них больше подходит для конкретного проекта. text поставляется в комплекте с установкой Go, что, естественно, делает ее очень популярной. Такие варианты, как quicktemplate, также могут быть хорошим выбором, если ваши шаблоны не нужно часто менять, а такие, как pongo2, проще использовать, если вам не очень нравится пользоваться стандартной библиотекой Go. Если вам требуется только подстановка строк, то fasttemplate также является отличным выбором с точки зрения производительности.

Оцените статью
devanswers.ru
Добавить комментарий