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

Время проверять, кричит внутренний инженер во мне.

Что мы хотим узнать?

  1. Сколько времени нужно, чтобы искать переменные окружения.
  2. Сколько времени нужно, чтобы найти нормальные переменные.
  3. Сколько времени нужно, чтобы найти переменные среды и заменить их значением по умолчанию, если оно не существует.

Покажи мне тест!

Хорошо, хорошо, хорошо… Через секунду.

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

Мы используем Go aka Golang. Мы используем пакет os, который является пакетом поиска среды по умолчанию, и, наконец, мы используем тесты производительности для получения информации о времени.

Хорошо, пусть начнутся испытания!

Давайте проверим, сколько времени занимает поиск переменной среды.

BenchmarkEnvLookup-8                    20000000         77.5 ns/op
--- BENCH: BenchmarkEnvLookup-8
    experiments_test.go:19: got output: ENV-Potatoes
    experiments_test.go:19: got output: ENV-Potatoes
    experiments_test.go:19: got output: ENV-Potatoes
    experiments_test.go:19: got output: ENV-Potatoes
    experiments_test.go:19: got output: ENV-Potatoes

77,5 нс / операцию. Что ж, довольно быстро воняет. Но против чего мы тестируем? Нам нужно проверить нормальный поиск переменных…

enchmarkStructLookup-8                 2000000000         0.28 ns/op
--- BENCH: BenchmarkStructLookup-8
    experiments_test.go:35: got output: STRUCT-Potatoes
    experiments_test.go:35: got output: STRUCT-Potatoes
    experiments_test.go:35: got output: STRUCT-Potatoes
    experiments_test.go:35: got output: STRUCT-Potatoes
    experiments_test.go:35: got output: STRUCT-Potatoes
    experiments_test.go:35: got output: STRUCT-Potatoes

ааааа, ну, переменные среды теперь кажутся довольно медленными… в 276 раз медленнее.

Обычная практика кодирования - поиск переменной среды и последующее присвоение значения по умолчанию, если его нет. Это именно то, чем я хотел заниматься. Так как же это выглядит в этом новом мировоззрении?

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

BenchmarkDoubleLookupBlankEnv-8         20000000          74.2 ns/op
--- BENCH: BenchmarkDoubleLookupBlankEnv-8
    experiments_test.go:56: got output: STRUCT-Potatoes
    experiments_test.go:56: got output: STRUCT-Potatoes
    experiments_test.go:56: got output: STRUCT-Potatoes
    experiments_test.go:56: got output: STRUCT-Potatoes
    experiments_test.go:56: got output: STRUCT-Potatoes

Здесь у нас есть допустимая переменная среды. Необходимо проделать ту же работу, что и раньше.

BenchmarkDoubleLookupValidEnv-8         20000000          77.8 ns/op
--- BENCH: BenchmarkDoubleLookupValidEnv-8
    experiments_test.go:82: got output: ENV-Potatoes
    experiments_test.go:82: got output: ENV-Potatoes
    experiments_test.go:82: got output: ENV-Potatoes
    experiments_test.go:82: got output: ENV-Potatoes
    experiments_test.go:82: got output: ENV-Potatoes

Так что, по сути, в нем не так много всего, всего несколько наносекунд, которые могли быть моим компьютером, делающим что-то еще. Но на это еще нужны годы. Поэтому нам нужно переосмыслить, как нам справляться с этой ситуацией.

Как насчет того, чтобы использовать структуру, и если она недействительна, используйте переменную среды.

BenchmarkUseStruct-8                    2000000000     0.54 ns/op
--- BENCH: BenchmarkUseStruct-8
    experiments_test.go:114: got output: STRUCT-Potatoes
    experiments_test.go:114: got output: STRUCT-Potatoes
    experiments_test.go:114: got output: STRUCT-Potatoes
    experiments_test.go:114: got output: STRUCT-Potatoes
    experiments_test.go:114: got output: STRUCT-Potatoes
    experiments_test.go:114: got output: STRUCT-Potatoes

Таким образом, использование struct var намного быстрее. Давайте для интереса посмотрим тот же код с допустимой переменной среды.

BenchmarkUseEnv-8                       20000000          78.5 ns/op
--- BENCH: BenchmarkUseEnv-8
    experiments_test.go:144: got output: 
    experiments_test.go:144: got output: 
    experiments_test.go:144: got output: 
    experiments_test.go:144: got output: 
    experiments_test.go:144: got output:

Хммммм, опять высокие цифры, но это было ожидаемо ...

Итак, осталось протестировать всего 2 вещи. Сравнение струн с «ничем». Можем ли мы сэкономить немного времени, используя лучшее сравнение строк?

Это 2 разных теста:

len(string) > 1
    or
string != ""

Тесты:

А теперь результаты:

BenchmarkLenLookup-8                    2000000000       0.27 ns/op
--- BENCH: BenchmarkLenLookup-8
    experiments_test.go:163: using output: 1
    experiments_test.go:163: using output: 1
    experiments_test.go:163: using output: 1
    experiments_test.go:163: using output: 1
    experiments_test.go:163: using output: 1
    experiments_test.go:163: using output: 1
BenchmarkStringLookup-8                 2000000000       0.27 ns/op
--- BENCH: BenchmarkStringLookup-8
    experiments_test.go:182: using output: 1
    experiments_test.go:182: using output: 1
    experiments_test.go:182: using output: 1
    experiments_test.go:182: using output: 1
    experiments_test.go:182: using output: 1
    experiments_test.go:182: using output: 1

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

Выводы

Использовать переменные среды просто и довольно быстро, я имею в виду, что 75 наносекунд - не совсем много времени. Однако это намного медленнее, чем просто поиск переменной (0,26 наносекунды) в структуре в 276 раз. Если вы собираетесь делать тысячи поисков в минуту. Тогда не используйте только переменные среды.

Переменные среды обычно не меняются. Обычно они устанавливаются, а затем происходит ваше исполнение. Так что нам даже не нужно постоянно их проверять. Можно с уверенностью предположить, что они будут одинаковыми в начале, на полпути и в конце выполнения. Во-вторых, если вы хотите, чтобы они изменились, это было бы серьезным событием для приложения, и вы могли бы сигнализировать об этом с помощью SIGHUP, который может вызвать новый поиск.

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

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

Поприветствуйте время поиска 0,26 наносекунды. Кажется, я только что сделал свое приложение в 276 раз быстрее… время выпить пива :)