Автор Аншул Гоял✏️
Библиотеки шаблонов являются важной частью экосистемы языка программирования. Стандартная библиотека Go предоставляет мощные библиотеки шаблонов из коробки, но в Go также есть множество библиотек шаблонов, созданных сообществом, которые могут использовать разработчики.
При наличии нескольких вариантов может быть трудно выбрать лучший вариант для конкретного случая использования.
В этой статье мы сравним несколько наиболее популярных библиотек шаблонов по производительности, функциональности и безопасности, используя инструмент бенчмаркинга Go. В частности, мы рассмотрим некоторые распространенные операции, такие как условные операции, вложенность шаблонов, циклы и т.д., используемые в шаблонах, и сравним их, чтобы дать вам представление о производительности и объеме памяти каждого шаблонизатора.
Я решил использовать четыре библиотеки шаблонизаторов Go в качестве основы для этой статьи, основываясь на использовании и применимости для разработчиков сегодня, так как некоторые другие библиотеки либо предварительно скомпилированы, либо поддерживаются редко. Библиотеки, которые мы будем сравнивать сегодня, это template/text
, pongo2
, liquid
и quicktemplate
.
Давайте начнем!
-
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
также является отличным выбором с точки зрения производительности.