Практическое продолжение с разделителями в C / x64 ASM
Я посмотрел статью под названием "Учебник по планированию параллелизма с форк-объединением и кражей работ". Я хочу реализовать кражу продолжения, где остальная часть кода после вызова spawn
имеет право быть украденным. Вот код из бумаги.
1 e();
2 spawn f();
3 g();
4 sync;
5 h();
Выбор дизайна импорта - это то, какую ветку предлагать воровским потокам. Используя рисунок 1, можно выбрать:
Детское воровство:
- Функция f() доступна для воровских потоков.
- Поток, который выполнил e(), выполняет g().
Продолжение кражи:
- Также называется "воровство родителей".
- Поток, который выполнил e(), выполняет f().
- Продолжение (которое затем вызовет g()) станет доступным для потоков вора.
Я слышал, что сохранение продолжения требует сохранения обоих наборов регистров (энергозависимых / энергонезависимых /FPU). В реализации волокна, которую я сделал, я реализовал воровство детей. Я читал о (теоретических) негативах кражи детей (неограниченное количество выполняемых задач, см. Статью для получения дополнительной информации), поэтому я хочу вместо этого использовать продолжения.
Я думаю о двух функциях, shift
а также reset
, где reset
разграничивает текущее продолжение и shift
уточняет текущее продолжение. Является ли то, что я спрашиваю, даже правдоподобным в среде Си?
РЕДАКТИРОВАТЬ: я думаю о создании reset
сохранить обратный адрес / NV GPR для текущего вызова функции (= строка 3) и сделать shift
передать управление следующему продолжению после возврата значения вызывающей стороне reset
,
1 ответ
Я реализовал кражу работы для HLL с именем PARLANSE, а не C на x86. PARLANSE используется ежедневно для создания символических параллельных программ производства в масштабе миллиона строк.
В общем, вы должны сохранить регистры как продолжения, так и "потомка". Учтите, что ваш компилятор может увидеть вычисление в f() и увидеть то же вычисление в g(), и может поднять это вычисление до точки непосредственно перед порождением, и поместить это вычисление в регистр, который f() и g () использовать как в подразумеваемом параметре. Да, это предполагает сложный компилятор, но если вы используете тупой компилятор, который не оптимизирует, почему вы пытаетесь идти параллельно для скорости?
В частности, однако, ваш компилятор может устроить регистры пустыми до вызова spawn, если он понимает, что означает spawn. Тогда ни продолжение, ни ребенок не должны сохранять регистры. (Компилятор PARLANSE фактически делает это).
Так что, сколько нужно сэкономить, зависит от того, насколько ваш компилятор готов помочь, и это зависит от того, знает ли он, что на самом деле делает spawn.
Ваш местный дружественный компилятор C, вероятно, не знает о вашей реализации spawn. Так что либо вы делаете что-то для принудительной очистки регистра (не спрашивайте меня, это ваш компилятор), либо вы смирились с тем, что лично вы не знаете, что находится в регистрах, и ваша реализация сохраняет их все для безопасности.
Если объем порожденной работы значителен, возможно, не имеет значения, сохраните ли вы все регистры. Тем не менее, x86 (и другие современные архитектуры), кажется, имеют огромное количество состояний, в основном в векторных регистрах, которые могут использоваться; в прошлый раз, когда я смотрел, это было больше 500 байт ~~ 100 записей в память, чтобы сохранить их, и ИМХО, это чрезмерная цена. Если вы не верите, что эти регистры будут передаваться из родительского потока в порожденный поток, то вы можете поработать над созданием порождения без регистров.
Если вы порождаете рутинные пробуждения с использованием стандартного механизма продолжения, который вы изобрели, то у вас есть вопросы о том, проходят ли ваши продолжения большое состояние регистра или нет. Та же проблема, те же решения, что и для spawn; Компилятор должен помочь, или вы лично должны вмешаться.
Вы найдете это очень весело.
[Если вы хотите сделать это действительно интересным, попробуйте нарезать потоки на время, если они углубляются в вычисления без случайного результата, вызывающего голодание потока. Теперь вы наверняка должны сохранить все состояние. Мне удалось заставить PARLANSE реализовать процесс порождения без сохранения регистров, но у меня было время на сохранение / восстановление состояния полного регистра, путем сохранения полного состояния во временном интервале и продолжения в специальном месте, которое пополняло все регистры до того, как оно передало управление временное местоположение ПК.