Далёкая память
В датацентрах давным давно уже проблемы с тем, чтобы закупать память -- она очень дорогая. Мы по сравнению с 2011 годом в Борге хоть и видим увеличение утилизации на 5-20%, но медианная утилизация остаётся на уровне 60% [1].
Из-за этого в статьях прошлых лет (года с 2018) сильно развивалось понятие далёкой памяти -- давайте мы абстрагируем память ещё, теперь будем забирать данные с помощью сети из других машин. Самые известные -- Hydra [2], мы на OSDI'22 в Google вышли с Carbink [3]. Обе статьи решают нужные нам задачи:
* Можем достигать лучшей утилизации и уменьшить простой
* Приложения могут аллоцировать больше памяти, чем одна машина
Проблемы очевидны: забирать по сети намного тяжелее, отказоустойчивость не так просто сделать (хранить память на SSD не подходит из-за ещё более медленных восстановлений), модель _памяти_ становится не очень ясной.
Для того, чтобы достигнуть репликации фактора два, можно реплицировать на три узла. Память дорогая и так делать не стоит. Поэтому лучше взять Erasure Coding: в самом простом случае это выглядит как поделить данные пополам, взять xor, теперь у нас есть 3 части, при выпадении любой, можно восстановить все данные. В итоге занимаем x1.5 места, но фактор репликации можно достигнуть любой. За Erasure Codes стоит большая теория, но основная идея, что мы делим данные на N несколько частей, считаем M дополнительных через линейное преобразование. При выпадении любых M, мы можем посчитать обратное линейное преобразование. Математика говорит, что матрицы существуют при почти всех разумных N и M, которые обладают таким свойством. Обычно выбирают что-то из пар (3,2), (4,3), (12,9).
Проблема в том, что когда вы аллоцируете память, её поделить даже на 3, 4 или 12 частей тяжело, так как аллокации объектов не очень большие и erasure codes начинают тормозить. Мы в Google вышли со статьёй о TCMalloc, где мы храним такие объекты в так называемых spans, много мелких объектов хранятся в одном чанке в пару мегабайт. Мы решили делать так же -- мы будем синхронизировать spans объектов (а также erasure codes на множество spans, что мы назвали spanset), а не каждый кусок. Это разгружает сеть, так как объекты в erasure codes находятся внутри спанов, а не разделены. Если ничего не выпало, из одного узла возьмутся данные, в худшем случае при падении узлов читать надо всё.
Изменения происходят в background тредах, если память становится холодной, она помещается в span, инвалидируется по сети, замещается. Узлы, которые отвечают за саму память, время от времени делают compaction, чтобы удалить ненужные spans. Если приложение memory intensive, это увеличивает нагрузку на сеть.
Использовать spans в пару мегабайт имеет лишние ненужные байты. В Carbink мы в итоге насчитали, что используем на 35% больше памяти, чем в Hydra, но зато сильно уменьшили 50 и 99 квантили (в ~1.2-1.5 раз) доступа и throughput.
В статье много интересных деталей -- например, как помечать биты в указателях, как брать RCU lock на указатели, которые приходят по сети и тд.
Меня, конечно, больше интересует вопрос о том, когда это можно ждать в датацентрах? Ну, в худшем случае никогда, в лучшем через 2-3 года, когда software и hardware stack будет к этому готов, потому что использование памяти по сети требует
* Писать немного кода, чтобы использовать такие указатели, прозрачно через аллокатор пока тяжело это сделать. Это кстати не особо большая проблема, сервисы обычно имеют понятное использование больших буфферов
* Perf будет страдать, надо будет аккуратно выбирать пользователей для этого, которым важен throughput, инфраструктура типа MapReduce/Spanner может подойти
* Можно сжимать холодую память в этой далёкой памяти, как делает, скажем, zswap, экономить ещё
Хорошая статья о том, как можно увеличивать утилизацию памяти, когда вы очень большие, ну и мы наконец-то поделились, что думаем о far memory и инвестируем research туда :)
[1] Borg: the next generation
[2] Hydra : Resilient and Highly Available Remote Memory
[3] Carbink: Fault-Tolerant Far Memory
В датацентрах давным давно уже проблемы с тем, чтобы закупать память -- она очень дорогая. Мы по сравнению с 2011 годом в Борге хоть и видим увеличение утилизации на 5-20%, но медианная утилизация остаётся на уровне 60% [1].
Из-за этого в статьях прошлых лет (года с 2018) сильно развивалось понятие далёкой памяти -- давайте мы абстрагируем память ещё, теперь будем забирать данные с помощью сети из других машин. Самые известные -- Hydra [2], мы на OSDI'22 в Google вышли с Carbink [3]. Обе статьи решают нужные нам задачи:
* Можем достигать лучшей утилизации и уменьшить простой
* Приложения могут аллоцировать больше памяти, чем одна машина
Проблемы очевидны: забирать по сети намного тяжелее, отказоустойчивость не так просто сделать (хранить память на SSD не подходит из-за ещё более медленных восстановлений), модель _памяти_ становится не очень ясной.
Для того, чтобы достигнуть репликации фактора два, можно реплицировать на три узла. Память дорогая и так делать не стоит. Поэтому лучше взять Erasure Coding: в самом простом случае это выглядит как поделить данные пополам, взять xor, теперь у нас есть 3 части, при выпадении любой, можно восстановить все данные. В итоге занимаем x1.5 места, но фактор репликации можно достигнуть любой. За Erasure Codes стоит большая теория, но основная идея, что мы делим данные на N несколько частей, считаем M дополнительных через линейное преобразование. При выпадении любых M, мы можем посчитать обратное линейное преобразование. Математика говорит, что матрицы существуют при почти всех разумных N и M, которые обладают таким свойством. Обычно выбирают что-то из пар (3,2), (4,3), (12,9).
Проблема в том, что когда вы аллоцируете память, её поделить даже на 3, 4 или 12 частей тяжело, так как аллокации объектов не очень большие и erasure codes начинают тормозить. Мы в Google вышли со статьёй о TCMalloc, где мы храним такие объекты в так называемых spans, много мелких объектов хранятся в одном чанке в пару мегабайт. Мы решили делать так же -- мы будем синхронизировать spans объектов (а также erasure codes на множество spans, что мы назвали spanset), а не каждый кусок. Это разгружает сеть, так как объекты в erasure codes находятся внутри спанов, а не разделены. Если ничего не выпало, из одного узла возьмутся данные, в худшем случае при падении узлов читать надо всё.
Изменения происходят в background тредах, если память становится холодной, она помещается в span, инвалидируется по сети, замещается. Узлы, которые отвечают за саму память, время от времени делают compaction, чтобы удалить ненужные spans. Если приложение memory intensive, это увеличивает нагрузку на сеть.
Использовать spans в пару мегабайт имеет лишние ненужные байты. В Carbink мы в итоге насчитали, что используем на 35% больше памяти, чем в Hydra, но зато сильно уменьшили 50 и 99 квантили (в ~1.2-1.5 раз) доступа и throughput.
В статье много интересных деталей -- например, как помечать биты в указателях, как брать RCU lock на указатели, которые приходят по сети и тд.
Меня, конечно, больше интересует вопрос о том, когда это можно ждать в датацентрах? Ну, в худшем случае никогда, в лучшем через 2-3 года, когда software и hardware stack будет к этому готов, потому что использование памяти по сети требует
* Писать немного кода, чтобы использовать такие указатели, прозрачно через аллокатор пока тяжело это сделать. Это кстати не особо большая проблема, сервисы обычно имеют понятное использование больших буфферов
* Perf будет страдать, надо будет аккуратно выбирать пользователей для этого, которым важен throughput, инфраструктура типа MapReduce/Spanner может подойти
* Можно сжимать холодую память в этой далёкой памяти, как делает, скажем, zswap, экономить ещё
Хорошая статья о том, как можно увеличивать утилизацию памяти, когда вы очень большие, ну и мы наконец-то поделились, что думаем о far memory и инвестируем research туда :)
[1] Borg: the next generation
[2] Hydra : Resilient and Highly Available Remote Memory
[3] Carbink: Fault-Tolerant Far Memory