Я разрабатываю систему, которая заранее компилирует байт-код CIL. Чтобы сохранить его относительно простым, а также сделать его очень переносимым, система будет выдавать исходный код C (однако со всеми конструкциями более высокого уровня, такими как разложение ООП) вместо машинного кода. Предполагается, что стандартный компилятор C для целевой платформы будет затем использоваться в этом коде для получения конечного продукта.

Изначально я намереваюсь использовать очень простой подход к сборке мусора, такой как stop-the-world. Однако, хотя приложение не требует выдающейся производительности, ему нужна достойная производительность, поэтому со временем может потребоваться замена GC.

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

Итак, мой вопрос: можно ли эффективно реализовать типичные барьеры записи в коде C? Можно предположить, что компилятор C имеет достойный оптимизатор. Также уже известно, что результирующий «исходный код» будет совершенно неразборчивым, поэтому ясность не имеет значения.

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

2
Kevin 17 Мар 2013 в 06:05

1 ответ

Лучший ответ

Я предполагаю, что вам нужен точный сборщик мусора, перемещающий или копирующий поколения.

У вас может быть барьер записи в C; Например, в средах выполнения Ocaml и MELT есть сборщик мусора поколений с барьером записи. А qish - это глобальный копирующий сборщик мусора с барьером записи, работающий с C.

(Кстати, MELT - это предметно-ориентированный язык для расширения GCC, и он скомпилирован в C, точно так же, как вы хотите это сделать)

Более важная проблема заключается в том, как вы храните локальные указатели (и как GC знает о них), это точный аспект вашего GC. Возможно, вы захотите упаковать их в какую-то локальную структуру .... Но тогда может случиться так, что компилятор C (например, GCC) оптимизирует немного меньше.

Вы можете заглянуть в исходный код последних версий MONO, у них есть GC с копированием поколений. Также загляните в Chicken Scheme (также генерирующую код C).

Обратите внимание, что ваш генератор кода C необходимо будет изменить, чтобы он соответствовал какой-либо (или вашей) конкретной реализации GC (поскольку каждый GC имеет несколько разные инварианты и ожидания). Позаботьтесь также о хвостовой рекурсии (некоторые компиляторы C , особенно последние версии GCC , может оптимизировать их в ограниченных случаях).

В Qish, MELT или Ocaml барьер записи (на стороне C) реализуется некоторыми макросами (или встроенными функциями), вызываемыми для каждого указателя, к которому прикоснулись. Детали зависят от реализации. Ваш генератор кода C позаботится о них.

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

4
Basile Starynkevitch 17 Мар 2013 в 12:51
Хорошая информация .. спасибо! Я могу пойти с частично консервативным движущимся GC поколений. Это позволяет избежать сложности и накладных расходов при вводе содержимого стека, но необходимо закрепить все, что выглядит как obj *, в стеке или регистрах процессора. В этой конкретной системе приложение управляется событиями, поэтому большинство вещей в любом случае не останется в стеке надолго.
 – 
Kevin
18 Мар 2013 в 23:41