В Clickhouse есть интересный код: при вызове одной функции происходит перевод области памяти исполняемого кода программы на использование Huge Pages. В процессе весь код программы копируется на новое место, память, использовавшаяся изначально для кода программы возвращается ОС, а потом запрашивается снова. Эта статья основана на соответствующей части доклада с Я.Субботника.

Сначала мы посмотрим, что такое виртуальная память и TLB, потом перейдём собственно к Clickhouse, посмотрим, почему там пришла идея делать такие махинации с бинарником в памяти, а в конце посмотрим, как это всё реализовано.

Виртуальная память

Как известно, программы на современных процессорах x86 не работают с физической памятью напрямую - вместо этого используется механизм виртуальной памяти. При этом, все адреса памяти, которые доступны программе - виртуальные. При каждом обращении к памяти процессор преобразует виртуальный адрес в физический. Для того, чтобы это работало, ОС настраивает систему таблиц страниц. Посмотрим, как это происходит на 32-битной системе. Вся память делится на страницы. Как правило, страница имеет размер 4Кб. На x86 адрес в памяти имеет 32 бита. Чтобы преобразовать виртуальный адрес в физический, процессор берёт верхние 10 бит адреса и читает элемент с таким номером из таблицы страниц. Там будет лежать адрес второй таблицы, в которой ищется запись под номером, соответствующим вторым 10 битам изначального адреса. Теперь уже процессор получает адрес начала искомой страницы памяти, прибавляет к нему нижние 12 бит адреса и получает физический адрес, по которому уже можно прочитать или записать данные.

Конечно, так, как описано, всё работало бы очень долго: мы хотели пройти по одному адресу из памяти, а чтобы сделать это, пришлось ещё два раза читать из памяти таблицы страниц.. Поэтому используется TLB (Translation lookaside buffer) - это этакий кеш.

На X86/64 (amd64) схема работы с памятью похожая, но, поскольку адрес уже 64-ёх битный, а не 32-ух, то уровней таблиц становится 4 или 5, вместо двух.

Большие страницы

Поскольку TLB имеет ограниченный и не очень большой размер, промахи по нему случаются, и это отнимает много времени. Разработчики ядра Linux предложили решение этой проблемы - Huge pages. Это страницы размера 2Мб или 1Гб. Логика такая: страницы больше - всего страниц меньше - меньше места в TLB нужно и реже случаются промахи. Всё должно работать быстрее.

Тесты производительности Clickhouse

Для того, чтобы можно было делать хорошие оптимизации для Clickhouse, там есть тесты производительности - при каждом коммите запускается множество performance тестов. Поскольку Clickhouse - это СУБД, то тесты представляют из себя различные запросы. В итоге по каждому запросу может быть 4 вердикта:

  1. Разница несущественная - нет статистически значимой разницы

  2. Запрос ускорился

  3. Запрос замедлился

  4. Запрос нестабилен - разброс времени выполнения запроса настолько велик, что вывод сделать невозможно.

Последний вариант - это не очень хорошо, потому что не даёт полезной информации влиянии коммита на производительность. Кроме того, некоторые нестабильные запросы могут не определяться как нестабильные, а случайный результат из первых трёх.

Почему же запросы могут быть нестабильными? Для того, чтобы понять, можно пытаться смотреть метрики:

  • userspace метрики - те, которые Clickhouse сам собирает. Сколько времени запрос выполнялся, сколько строк обработано и так далее

  • метрики ОС: сколько времени программа ожидала очереди на запуск и тд

  • метрики процессора: сколько обработано инструкций, количество промахов L1 кеша и TLB

Странный Pull Request

В какой-то момент появился pull request, в котором добавили поддержку типов Int256, Uint256, Decimal256 для подсчёта денег в криптовалютах. В результате вырос размер бинарника (от 324 Мб до 429 Мб). Замедлений тестов производительности обнаружено не было, но выросло количество нестабильных тестов.

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

Решение - засунуть сегмент памяти с кодом программы в память, использующую Huge Pages!

Реализация

Код, который делает всю магию, находится здесь.

Итак, нам надо как-то заставить ОС использовать большие страницы для памяти с инструкциями программы.
Казалось бы, можно сделать системный вызов madvise и сказать операционной системе использовать большие страницы для нужного участка памяти.

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

Что же тогда делать? Есть безумное решение - надо переаллоцировать код на лету:

  1. Выделить новый кусок памяти и попросить ОС использовать для него большие страницы

  2. Скопировать туда весь код программы

  3. Сделать этот код исполняемым

  4. Передать туда исполнение

  5. Удалить отображение старого участка памяти с кодом, сделав munmap.

Будет ли это работать? Конечно, нет. Все вызовы функций, условные и безусловные переходы будут пытаться перейти на исполнение инструкций из старого куска памяти, для которого большие страницы не включены, так как при сборке Clickhouse не включено Position Independent Code из соображений производительности. При таком переходе произойдёт Segmentation Fault, потому что старой памяти уже нет или там лежит что-то другое.

Но можно сделать немного по-другому.

  1. Выделить новый кусок памяти

  2. Скопировать туда весь код программы

  3. Сделать этот код исполняемым

  4. Перейти туда

  5. Удалить отображение старого участка с кодом, вызвав munmap

  6. Выделить участок памяти по старому адресу, такого же размера, как и был

  7. С помощью madvise попросить ОС использовать для него большие страницы

  8. Переписать код на этот участок памяти, пометить его как исполняемый и перейти на него

  9. Победа!

Будем смотреть, как это сделать.

Функция getMappedArea просматривает список отображений в память данного процесса и находит то, в котором находится переданный ей указатель - если передать туда адрес какой-нибудь функции, она вернёт то отображение, где находится код программы.

getMappedArea
std::pair<void *, size_t> getMappedArea(void * ptr)
{
    using namespace DB;

    uintptr_t uintptr = reinterpret_cast<uintptr_t>(ptr);
    ReadBufferFromFile in("/proc/self/maps");

    while (!in.eof())
    {
        uintptr_t begin = readAddressHex(in);
        assertChar('-', in);
        uintptr_t end = readAddressHex(in);
        skipToNextLineOrEOF(in);

        if (begin <= uintptr && uintptr < end)
            return {reinterpret_cast<void *>(begin), end - begin};
    }

    throw Exception("Cannot find mapped area for pointer", ErrorCodes::LOGICAL_ERROR);
}

Функция remapExecutable вызывает функцию getMappedArea, после чего переходит на первый шаг нашего процесса в функции remapToHugeStep1.

remapExecutable
size_t remapExecutable()
{
    auto [begin, size] = getMappedArea(reinterpret_cast<void *>(remapExecutable));
    remapToHugeStep1(begin, size);
    return size;
}

Функция remapToHugeStep1 выделяет новый кусок памяти с помощью mmap, копирует туда весь код программы функцией memcpy, после чего переходит к функции remapToHugeStep2, но не просто так, а переходя на код на новом участке памяти.

remapToHugeStep1
__attribute__((__noinline__)) void remapToHugeStep1(void * begin, size_t size)
{
    /// Allocate scratch area and copy the code there.

    void * scratch = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (MAP_FAILED == scratch)
        throwFromErrno(fmt::format("Cannot mmap {} bytes", size), ErrorCodes::CANNOT_ALLOCATE_MEMORY);

    memcpy(scratch, begin, size);

    /// Offset to the scratch area from previous location.

    int64_t offset = reinterpret_cast<intptr_t>(scratch) - reinterpret_cast<intptr_t>(begin);

    /// Jump to the next function inside the scratch area.

    reinterpret_cast<void(*)(void*, size_t, void*)>(reinterpret_cast<intptr_t>(remapToHugeStep2) + offset)(begin, size, scratch);
}

На момент входа в функцию remapToHugeStep2 регистр instruction pointer (eip) находится на новом участке памяти, поэтому старый можно удалять. К сожалению, нельзя использовать библиотечные функции, поэтому используются их замены.

remapToHugeStep2 удаляет отображение бинарника в старый участок памяти с кодом с помощью munmap, затем там же делает анонимное отображение того же размера и просит ОС использовать для него большие страницы. Затем код программы копируется в это новое отображение и оно помечается как разрешённое только для чтения и исполнение (чтобы больше нельзя было туда писать). В конце концов мы переходим в функцию remapToHugeStep3, но уже в новом участке памяти.

remapToHugeStep2
__attribute__((__noinline__)) void remapToHugeStep2(void * begin, size_t size, void * scratch)
{
    /** Unmap old memory region with the code of our program.
      * Our instruction pointer is located inside scratch area and this function can execute after old code is unmapped.
      * But it cannot call any other functions because they are not available at usual addresses
      * - that's why we have to use "our_syscall" function and a substitution for memcpy.
      * (Relative addressing may continue to work but we should not assume that).
      */

    int64_t offset = reinterpret_cast<intptr_t>(scratch) - reinterpret_cast<intptr_t>(begin);
    int64_t (*syscall_func)(...) = reinterpret_cast<int64_t (*)(...)>(reinterpret_cast<intptr_t>(our_syscall) + offset);

    int64_t munmap_res = syscall_func(SYS_munmap, begin, size);
    if (munmap_res != 0)
        return;

    /// Map new anonymous memory region in place of old region with code.

    int64_t mmap_res = syscall_func(SYS_mmap, begin, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
    if (-1 == mmap_res)
        syscall_func(SYS_exit, 1);

    /// As the memory region is anonymous, we can do madvise with MADV_HUGEPAGE.

    syscall_func(SYS_madvise, begin, size, MADV_HUGEPAGE);

    /// Copy the code from scratch area to the old memory location.

    {
        __m128i * __restrict dst = reinterpret_cast<__m128i *>(begin);
        const __m128i * __restrict src = reinterpret_cast<const __m128i *>(scratch);
        const __m128i * __restrict src_end = reinterpret_cast<const __m128i *>(reinterpret_cast<const char *>(scratch) + size);
        while (src < src_end)
        {
            _mm_storeu_si128(dst, _mm_loadu_si128(src));

            ++dst;
            ++src;
        }
    }

    /// Make the memory area with the code executable and non-writable.

    syscall_func(SYS_mprotect, begin, size, PROT_READ | PROT_EXEC);

    /** Step 3 function should unmap the scratch area.
      * The currently executed code is located in the scratch area and cannot be removed here.
      * We have to call another function and use its address from the original location (not in scratch area).
      * To do it, we obtain its pointer and call by pointer.
      */

    void(* volatile step3)(void*, size_t, size_t) = remapToHugeStep3;
    step3(scratch, size, offset);
}

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

remapToHugeStep3
__attribute__((__noinline__)) void remapToHugeStep3(void * scratch, size_t size, size_t offset)
{
    /// The function should not use the stack, otherwise various optimizations, including "omit-frame-pointer" may break the code.

    /// Unmap the scratch area.
    our_syscall(SYS_munmap, scratch, size);

    /** The return address of this function is pointing to scratch area (because it was called from there).
      * But the scratch area no longer exists. We should correct the return address by subtracting the offset.
      */
    __asm__ __volatile__("subq %0, 8(%%rsp)" : : "r"(offset) : "memory");
}

Вот и всё, в общем-то.

Выводы

  1. Всё работает, Huge Pages используются

  2. Количество iTLB промахов уменьшилось почти до нуля

  3. Скорость работы никак не изменилась

  4. Как это повлияло на число нестабильных тестов - в докладе не сказано. Если найдёте эту информацию - подскажите.

Комментарии (28)


  1. mobi
    08.11.2022 09:08
    +1

    Не совсем понятно, зачем выделять remapToHugeStep3 в отдельную функцию и делать ассемблерную вставку, когда в стеке и так лежит адрес возврата в remapToHugeStep1?


    1. vda19999 Автор
      08.11.2022 09:31

      remapToHugeStep3 - это отдельная функция, потому что надо было перенести исполнение на новую-старую область памяти, чтобы новую (временную) область удалить.


      1. mobi
        08.11.2022 09:34

        Я про то, что можно было вернуться в remapToHugeStep1 (обычным ret'ом) и удалять временную область оттуда.


        1. vda19999 Автор
          08.11.2022 10:21

          Вы правы, кажется.

          Наверное, ответ - потому что автору кода так показалось логичнее


  1. arteast
    08.11.2022 10:35
    +1

    Вот тут с идеями об использовании huge pages для кода уже игрался: https://easyperf.net/blog/2022/09/01/Utilizing-Huge-Pages-For-Code

    Причем в одном из подходов в huge pages грузится, похоже, код прямо из (специально подготовленного) файла штатно без каких-либо телодвижений. Т.е. " Linux позволит использовать большие страницы только для анонимных отображений в память - то есть, для тех, которые не связаны ни с каким файлом " не вполне верно. На что также намекает строчкаFileHugePages в /proc/meminfo


    1. vda19999 Автор
      08.11.2022 11:07

      В man про madvise действительно сказано, что он может сделать большими только страницы анонимных отображений.

      В докладе упоминается возможность использовать большие страницы другим способом, не с помощью madvise, но он не подходил из-за того, как запускали тесты производительности.


      1. arteast
        08.11.2022 11:24
        +1

        Глянул одним глазком на доклад (надо будет посмотреть целиком :)) - действительно, товарищи эту статью видели и им действительно это не подходит; им нужно кровь из носу использовать Transparent Huge Pages, потому они и занимаются нехорошими вещами.

        "Нормальному" же пользователю, которому надо сервис почему-либо загнать в HP, будет в 95% случаях проще настроить линукс, как описано в той статье )


        1. arteast
          10.11.2022 14:39

          Случайно наткнулся на патч от Google, где они решали ту же проблему для Chrome; там фиксили mremap с thp. Т.е. может быть в новых версиях Linux можно вместо всей этой чешуйни просто после копирования данных сделать mremap их по оригинальному адресу; но надо смотреть на выравнивание адресов данных.

          Еще одна альтернатива была бы вынести весь функционал по переезду данных с page cache на anonymous в отдельный DSO, и тогда не надо было бы городить весь этот изврат с написанием кода, работающего в любом месте в памяти.


          1. vda19999 Автор
            10.11.2022 15:57

            Я что-то ничего не понял. Вы не могли бы прислать ссылку на этот патч?


            1. arteast
              10.11.2022 16:19

              https://github.com/torvalds/linux/commit/550a7d60bd5e35a56942dba6d8a26752beb26c9f

              И использование - https://chromium.googlesource.com/chromium/src/+/refs/heads/main/chromeos/hugepage_text/hugepage_text.cc - выделяют блок hugepage памяти (выровненный по границе большой страницы), копируют данные, задают правильные права, а потом вместо всех телодвижений по переезду - просто делают mremap этого блока по исходному адресу (тоже выровненному по границе большой страницы - это важно). Имеющиеся маппинги автоматически закрываются, hugepages переезжают на новый виртуальный адрес и все.


  1. staticmain
    08.11.2022 14:02
    +4

     Иными словами, из-за того, что размер бинарника сильно вырос, количество TLB промахов по адресам в коде увеличилось, и это могло привести к росту числа нестабильных запросов.

    Кажется, что изначально решается не та проблема, которая должна. Тут какой-то огромный монолит на полгига бинарника. При любом подходе будут и cache misses и page reloads. Просто банально из-за размера. Первое, что тут нужно сделать - проанализировать блоки бинарника, выделить структурные части. Выделить те из них, которые не требуют мгновенного отлупа в отдельные бинари, которые либо держать как демоны, либо запускать однократно для нужных операций. Если таких мало и глобально размер не упал, то делать несколько резидентных главных сервисов, между которыми настраивать связь, например по Unix Socket.

    Увеличение размера бинаря (!) на 100 МБ из-за добавления нескольких абстракций для длинной арифметики - это полный ужас. Значит там накручено такое количество уровней абстракций и фреймворков, что это уже давным давно bloatware, который срочно нуждается в анализе и декомпозиции. Чем дальше это тянется - тем больше нужно будет в конце концов применить ресурсов на то, чтобы добавить какую-то одну фичу.

    Отсюда уже появляются все эти пляски вприсядку с transparent HP, после которой разработчики с гордым видом ходят по конференциям и рассказывают какие они молодцы, что вот, они сделали, но забывают упомянуть, почему они так сделали.

    Как только появляется нужда в чем-то нестандартном (особенно в linux, который максимально гибок в настройках памяти и производительности) - это значит, что архитектура приложения либо построена неправильно, либо на нее давно забили и просто лепят снаружи костыли, которые дешевле обойти перекладыванием бинаря в HP, чем исправить саму основу.


    1. blind_oracle
      08.11.2022 19:20
      +1

      Архитектура Кликхауса такова что они собирают всё в единый статический бинарник со всеми зависимостями. А т.к. это СУБД то там слишком дорого ходить по юникс сокету в другой демон по любому вопросу.

      Ну КХ огромный проект с кучей зависимостей - такой размер бинарника оправдан.

      Существенное увеличение бинарника из-за 256-битных типов действительно странно, надо глядеть почему так получилось.


      1. staticmain
        08.11.2022 19:29

        Вы разработчик clickhouse? То что это СУБД не означает, что там нечего выносить за пределы единого бинаря. И единый бинарь со всеми зависимостями - это хорошо, чтобы закинуть portable версию на флешку. Большой солидный проект никто так применять не будет. Для большого проекта это даже хуже, потому что чтобы залатать дыру в безопасности нельзя будет просто сделать инкрементальный апдейт заменя одну версию openssl на другую и пропатчив это "на лету". Только перекомпилировать или перевыкачивать весь дистрибутив.


        1. blind_oracle
          08.11.2022 19:44
          +1

          Вы разработчик clickhouse? 

          В том числе, контрибутил туда, плюс давно с ним работаю.

          То что это СУБД не означает, что там нечего выносить за пределы единого бинаря.

          Означает.

          В СУБД любая часть кода должна быть доступна сразу т.к. запрос должен быть обработан максимально быстро.

          Если это потребует ещё и сериализации данных чтобы запихать их в Pipe и разобрать с той стороны другим демоном, дождаться ответа потом - производительность упадёт ниже плинтуса.

          У них даже хеш-функций различных реализовано штук 60 в коде чтобы использовать оптимальную в каждом случае - производительность складывается из мелочей.

          Большой солидный проект никто так применять не будет.

          Ну, вам конечно виднее, чем разработчикам самой быстрой колоночной СУБД на рынке... Я думаю что они этот вопрос продумывали и остановились на таком подходе по сумме причин.

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

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


          1. staticmain
            08.11.2022 22:53
            +1

            контрибутил туда, плюс давно с ним работаю.

            Ну то есть не разработчик архитектуры в том числе, а человек, который поправил несколько багов/добавил что-то мелкое

            Означает

            Смелое заявление.

            В СУБД любая часть кода должна быть доступна сразу т.к. запрос должен быть обработан максимально быстро.

            А еще огромное количество кода, которое проверяет целостность СУБД, проводит периодические селф-чеки, инициализацию, деиницализацию, сброс кэша на диск (где задержка даже в несколько секунд вообще не важна).

            разработчикам самой быстрой колоночной СУБД на рынке

            Ууух, какое смелое заявление [2], а пруфы будут? Или только рекламные буклеты от самого яндекса? Да даже по спекам самого clickhouse он допускает задержки выполения до 50 мс, а запросы должны быть в пределах 100 rps. Где тут нужда в сверхскоростном взаимодействии?


        1. themen2
          09.11.2022 22:42

          А как насчёт солидного проекта под названием ВК. Бекенд - это vk.bin на 4гб на с++. Где то была статья тут на эту тему. И ничего, все работает. А тут каких то жалких 400м;)


          1. staticmain
            10.11.2022 23:22

            И ничего, все работает

            Охохох. ВК - та еще помойка, даже в плане кодинга. Достаточно было посмотреть видео от Щербака о том, как они боролись с транскодингом видео и дебажили в стиле "поломалось или нет", чтобы понять что структурой там и не пахнет. ВК - такое же нагромождение многолетних костылей, как и Oracle, как и Windows.

            К слову, ВК настолько хорош, что в его багтрекере сейчас 4417 открытых бага.


            1. themen2
              11.11.2022 00:20

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

              Так как ВК это большой проект, то там скорее всего оправданы специализированные базы данных, а не общего назначения. Если написать свою бд, то можно очень гибко управлять хранением данных (а значит и экономить сильно место на дисках), также быстрее обрабатывать запросы, чем в бд общего назначения.

              Все таки вы по одному видео заявляете, что ВК в плане кода дерьмо. Согласитесь это странно. Чтобы так сказать, вам надо оценить все проекты, весь код.

              А что такое хорошая структура большого выконагруженного проекта по вашему?

              Чтобы говорить хорошая надо сначала определить критерий хорошести - удобство чтения и сопровождения кода, скорость работы кода, милимальное кол-во кода идр? Какой критерий? Не забывем, что на их объемах память стоит дорого и надо экономить

              Ps: отдельно хочу заметить , что не всем проектам надо копировать структуру и подходы от крупных проектов. А 95% случаев достаточно распространенных проверенных стеков, реляционной СУБД итд. И не надо лезть в какие то дебри. Вот когда проект сильно начитает нагружаться пользователями, тогда уже надо думать. Но до этого в 90% случаях просто не дойдет;)


      1. FruTb
        08.11.2022 20:55
        +2

        Существенное увеличение бинарника из-за 256-битных типов действительно странно, надо глядеть почему так получилось

        Есть подозрение что там все внутри на генериках и добавление типов вызвало "комбинаторный взрыв" тк понадобилось синтезировать код для всех новых зависимых типов (конверторы, сериализаторы, проверки итд).


        1. staticmain
          08.11.2022 22:54
          +1

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


          1. themen2
            09.11.2022 22:45
            -1

            А вы - разработчик СУБД, чтобы что то заявлять по поводу как надо детаь правильно СУБД?


            1. staticmain
              10.11.2022 23:30

              Я больше 10 лет занимаюсь низкоуровневым программированием, из которых последние лет 5 - в качестве лида или руководителя направления. Я участвовал в разработке систем контроля безопасности для атомных электростанций РБМК-типа, проекты, в которых я принимал значительное участие сейчас помогают разрабатывать новые пьезоматериалы (привет тем, кто меня "вычислил"), автоматизируют кинотеатры, доставляют шифрованный контент, проводят кассовые продажи, часто системы приходилось либо целиком проектировать с нуля, либо перерабатывать старые legacy-системы, в которые либо был неподдерживаемый монолит либо нагромождение костылей 6-8-летней давности.
              Отвечая на ваш вопрос - с нуля СУБД не разрабатывал, но подсистемы хранения данных нужны почти в любом проекте и там либо выбирается СУБД "по вкусу", либо (в условиях экономии ресурсов) реализуется что-то миниатюрное под нужды проекта. Ну и год я работал в оф.партнере Oracle, где разгребал их нагромождения PL/SQL, где чтобы поменять одно значение в отчете нужно было добавить миллион новых функций, чтобы не сломать старые.


    1. apro
      08.11.2022 22:28
      +2

      А точно есть смысл разбиения на отдельные бинарники? С точки зрения "page cache" Linux ядра совершенно не важно отдельные бинарники или это единый, загружены будут только используемые страницы, и в памяти будут находиться именно используемые страницы.

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


  1. knutov
    09.11.2022 04:08

    А будет ли теперь работать CH, если huge pages нет? Например в ряде вариантов контейнерной виртуализации?


    1. vda19999 Автор
      09.11.2022 07:09

      Да. Huge pages включаются только для тестов производительности, в production их нет


    1. oller
      09.11.2022 07:15

      Ps извините если ответ будет не грамотным, отвечаю исходя из своего опыта работы с кликхаус

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


  1. IGR2014
    11.11.2022 01:51

    Из статьи понял всё кроме одного: что, блин, такое Clickhouse и для чего оно надо?!))

    И зачем ему работа с TLB?!


    1. vda19999 Автор
      11.11.2022 07:04

      ClickHouse — это колоночная аналитическая СУБД с открытым кодом, использующая диалект SQL. Её особенность в том, что она колоночная, и за счёт этого многие запросы работают быстрее, чем в mysql или postgres

      C TLB работает любая программа на компьютере - он используется для кеширования маппинга страниц виртальной памяти в страницы физической памяти. Без него все программы работали бы очень медленно