Start Debugging

Создание движка базы данных с микросекундной задержкой на C#

Проект Typhon Лоика Бауманна нацелен на ACID-коммиты за 1-2 микросекунды с использованием ref struct, аппаратных интринсиков и закреплённой памяти, доказывая, что C# может конкурировать на уровне системного программирования.

Предположение, что высокопроизводительные движки баз данных требуют C, C++ или Rust, глубоко укоренилось. Проект Typhon Лоика Бауманна бросает этому прямой вызов: встраиваемый ACID-движок базы данных, написанный на C#, нацеленный на транзакционные коммиты за 1-2 микросекунды. Проект недавно попал на главную страницу Hacker News, вызвав оживлённую дискуссию о том, что современный .NET действительно может делать.

Инструментарий производительности в современном C#

Ключевой аргумент Бауманна — что узким местом в дизайне движков баз данных является раскладка памяти, а не выбор языка. Современный C# предоставляет инструменты для контроля памяти на уровне, который был бы невозможен десятилетие назад.

Типы ref struct живут исключительно на стеке, устраняя выделения в куче на горячих путях:

ref struct TransactionContext
{
    public Span<byte> WriteBuffer;
    public int PageIndex;
    public bool IsDirty;
}

Для областей памяти, которые никогда не должны перемещаться, GCHandle.Alloc с GCHandleType.Pinned держит сборщик мусора подальше от критических участков. В сочетании с [StructLayout(LayoutKind.Explicit)] вы получаете контроль на уровне C над каждым байтовым смещением:

[StructLayout(LayoutKind.Explicit, Size = 64)]
struct PageHeader
{
    [FieldOffset(0)]  public long PageId;
    [FieldOffset(8)]  public long TransactionId;
    [FieldOffset(16)] public int RecordCount;
    [FieldOffset(20)] public PageFlags Flags;
}

Аппаратные интринсики для горячих путей

Пространство имён System.Runtime.Intrinsics даёт прямой доступ к SIMD-инструкциям. Для движка базы данных, сканирующего страницы или вычисляющего контрольные суммы, это разница между “достаточно быстро” и “конкурентоспособно с C”:

using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

static unsafe uint Crc32Page(byte* data, int length)
{
    uint crc = 0;
    int i = 0;
    for (; i + 8 <= length; i += 8)
        crc = Sse42.Crc32(crc, *(ulong*)(data + i));
    for (; i < length; i++)
        crc = Sse42.Crc32(crc, data[i]);
    return crc;
}

Принуждение дисциплины во время компиляции

Один из самых интересных аспектов подхода Typhon — использование анализаторов Roslyn в качестве защитных перил. Пользовательские анализаторы навязывают доменно-специфичные правила (отсутствие случайных выделений в куче в транзакционном коде, отсутствие непроверенной арифметики указателей вне утверждённых модулей) во время компиляции, вместо того чтобы полагаться на обзор кода.

Ограниченные дженерики с where T : unmanaged обеспечивают ещё один уровень, гарантируя, что обобщённые структуры данных работают только с blittable-типами, имеющими предсказуемые раскладки памяти.

Что это значит для .NET

Typhon ещё не продакшн-база данных. Но проект демонстрирует, что разрыв между C# и традиционными системными языками значительно сократился. Между Span<T>, аппаратными интринсиками, ref struct и явным контролем раскладки памяти, .NET 10 даёт вам строительные блоки для критически важной по производительности системной работы, не покидая управляемую экосистему.

Полный материал стоит прочитать ради архитектурных деталей и бенчмарков.

< Назад