Одной из областей, где мы считаем, что Voron можно улучшить, является политика использования свободного пространства. В частности, более рациональное использование свободного пространства может привести к повышению производительности, так как нам не нужно будет так много искать.
Я потратил некоторое время, работая над этим, и получил кое-что, что, по крайней мере на бумаге, выглядит намного лучше, с точки зрения производительности. Но … фактические тесты показали незначительное улучшение, а в некоторых случаях реальную деградацию! Это был момент, когда я понял, что на самом деле мне нужен какой-то контроль, чтобы увидеть, какой сценарий будет для нас абсолютно оптимальным. Так что я написал нулевую политику свободного пространства. Без свободного места Ворон всегда будет идти до конца файла, предоставляя нам лучший сценарий последовательной записи.
Это дает нам следующее поведение:
Flush 1 with 2 pages - 8 kb writes and 1 seeks ( 2 leaves, 0 branches, 0 overflows) Flush 2 with 8 pages - 32 kb writes and 1 seeks ( 7 leaves, 1 branches, 0 overflows) Flush 3 with 10 pages - 40 kb writes and 1 seeks ( 9 leaves, 1 branches, 0 overflows) Flush 27 with 74 pages - 296 kb writes and 1 seeks ( 72 leaves, 2 branches, 0 overflows) Flush 28 with 74 pages - 296 kb writes and 1 seeks ( 72 leaves, 2 branches, 0 overflows) Flush 29 with 72 pages - 288 kb writes and 1 seeks ( 70 leaves, 2 branches, 0 overflows) Flush 1,153 with 155 pages - 620 kb writes and 1 seeks (102 leaves, 53 branches, 0 overflows) Flush 1,154 with 157 pages - 628 kb writes and 1 seeks (104 leaves, 53 branches, 0 overflows) Flush 1,155 with 165 pages - 660 kb writes and 1 seeks (108 leaves, 57 branches, 0 overflows) Flush 4,441 with 191 pages - 764 kb writes and 1 seeks (104 leaves, 87 branches, 0 overflows) Flush 4,442 with 196 pages - 784 kb writes and 1 seeks (107 leaves, 89 branches, 0 overflows) Flush 4,443 with 198 pages - 792 kb writes and 1 seeks (108 leaves, 90 branches, 0 overflows) Flush 7,707 with 200 pages - 800 kb writes and 1 seeks (106 leaves, 94 branches, 0 overflows) Flush 7,708 with 204 pages - 816 kb writes and 1 seeks (106 leaves, 98 branches, 0 overflows) Flush 7,709 with 211 pages - 844 kb writes and 1 seeks (113 leaves, 98 branches, 0 overflows) Flush 9,069 with 209 pages - 836 kb writes and 1 seeks (107 leaves, 102 branches, 0 overflows) Flush 9,070 with 205 pages - 820 kb writes and 1 seeks (106 leaves, 99 branches, 0 overflows) Flush 9,071 with 208 pages - 832 kb writes and 1 seeks (108 leaves, 100 branches, 0 overflows)
И с этим, 10000 транзакций с 100 случайными значениями каждая:
fill rnd buff separate tx : 106,383 ms 9,400 ops / sec
И это говорит мне о том, что в лучшем случае, есть что-то еще, что вызывает эту проблему, и это не стоимость поиска. Я сократил количество транзакций до 500 и запустил его через профилировщик, и я получил следующее:
Другими словами, почти все время было потрачено на вызов FlushViewOfFile. Однако я думаю, что мы уже достаточно оптимизировали это, не так ли? Глядя на вызовы, кажется, что у нас есть только один FlushViewOfFile на транзакцию в этом сценарии.
Фактически, глядя на реальное поведение системы, мы можем увидеть:
Так что ищем, у нас все хорошо. Однако я не могу понять, почему мы видим эти вызовы ReadFile. Глядя на данные, кажется, что мы сталкиваемся с этим всякий раз, когда получаем доступ к текущей части файла, так что это подсистема mmap, делающая страницы содержимого файла в памяти, прежде чем мы начнем делать это. Это на самом деле очень здорово, что он способен страница 1 МБ за раз.
Далее, давайте посмотрим, что еще мы можем сделать здесь. Я провел тест 500 TX на жестком диске, и он дал мне следующий результат:
fill rnd sync separate tx : 25,540 ms 1,958 ops / sec
Но обратите внимание, что каждая запись имеет две записи. Один в конце файла и один в начале файла (что является фактическим последним актом фиксации). Что случилось, если мы просто удалили эту часть?
Это дало мне совсем другое число:
fill rnd sync separate tx : 21,764 ms 2,297 ops / sec
Так что поиск и запись одной страницы стоили нам 17% нашей производительности. Вот подробности запуска этого теста:
Теперь это бессмысленный тест, добавленный только для проверки относительных затрат. Мы должны сделать запись заголовка, иначе мы не можем сделать реальные транзакции.
Ради интереса, я запустил то же самое, используя последовательные записи, что дало мне 3619 операций в секунду. Поскольку в обоих случаях мы на самом деле делаем последовательные записи, основным отличием было то, сколько мы на самом деле написали. Это вид записи последовательно:
Как видите, нам нужно всего лишь записать от 8 до 10 страниц за транзакцию, а в случайном случае — от 110 до 130. И это, очевидно, имеет много последствий.
Все это научило меня чему-то очень важному. В конце концов, актуальная политика свободного пространства имеет значение, но не так много. Поэтому мне нужно выбрать что-то хорошее, но это все.