Imax9
NEWS   ARTICLES   MINIMIG   FILES   ABOUT

Два потока лучше, чем один...

Предлагаю вам перевод цикла The EightThirtyTwo ISA Part 10: Two threads are better than one… автора Alastair M. Robinson.

Одна из главных целей конвейерного процессора - постоянно поддерживать конвейер полным как можно большее время. Самым большим препятствием на пути к этой цели является существование "опасностей" – ситуаций, когда то, что должно произойти в начале конвейера, зависит от результата, который еще предстоит определить в дальнейшем. Это особая слабость процессора EightThirtyTwo: поскольку у нас всего восемь регистров, а многие операции связаны с регистром tmp, есть большая вероятность, что любая предоставленная инструкция будет зависеть от результата предыдущей. Один из способов обойти это-использовать переадресацию результатов, когда, например, результат, вычисленный в ALU, может быть отправлен непосредственно на входы ALU для следующей инструкции, а не сначала записан, а затем снова считан из файла регистра.

Я еще не пытался реализовать это для EightThirtyTwo. Вместо этого я попытался сделать что – то гораздо менее разумное для легковесного процессора - двухпоточность!

Идея двойной потоковой передачи заключается в том, что два независимых потока команд гарантированно не будут зависеть друг от друга, поэтому количество опасностей значительно уменьшается, а общая пропускная способность увеличивается, хотя каждый поток немного замедляется. Каждый поток требует своей собственной логики выборки, своего собственного декодера и собственного файла регистров, поэтому излишне говорить, что логический след двухпоточного режима значительно больше, чем однопоточный режим, но не вдвое больше, а где-то на 50-60%. (Если это кажется вам слишком большим, помните, что EightThirtyTwo в настоящее время использует логику, а не block RAM для своего регистрационного файла, и теперь у нас есть их два.)

Следующий вопрос, который следует рассмотреть, - как обрабатывать запуск, когда есть два независимых счетчика команд: обозначаем ли мы два разных адреса в качестве векторов запуска/сброса или есть лучший способ?

После некоторых размышлений я решил расширить решение, которое я выбрал для прерываний, а именно, используя один и тот же адрес запуска для обоих потоков, но с разными кодами условий. Это похоже на то, как работает функция Posix fork () – два процесса расходятся на основе возвращаемого значения fork().

Флаги условий, когда поток программы попадает в местоположение 0, теперь определяются следующим образом:

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

#include "assembler.pp"

	.section .text.startup

_start:
	cond	SGT	//  флаг Z и C flag оба очищены -> Thread 1
		li	IMW0(PCREL(.start1))
		add	r7

	cond	SLT	// флаг Z очищен, флаг C установлен -> Thread 2
		li	IMW0(PCREL(.start2))
		add	r7

	cond	GE	// флаг Z установлен (путем исключения), флаг C очищен
		li	IMW0(PCREL(.interrupt))
		add	r7

	// Путем исключения, флаг Z установлен, флаг C установлен - зарезервировано.
.spin:
	cond NEX
	li	IMW0(PCREL(.spin))
	add	r7

.start1:
	...

Я обнаружил, что двухпоточный режим несколько усложнил логику опасности и значительно снизил максимальную скорость дизайна, что побудило меня сделать некоторый редизайн. Логика опасности теперь несколько менее запутана и более читабельна, и на моей плате DE2 теперь у меня есть демонстрационный двухпоточный проект, работающий на частоте 133 МГц. Я еще не определил максимальную надежную скорость в однопоточном режиме, но я почти уверен, что критический путь - это умножитель.

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

Так что я могу использовать процессор в двухпоточной конфигурации в реальном проекте? Честно говоря, вероятно, нет – логический след больше, чем хотелось бы в двухпоточном режиме, но я узнал достаточно в этом процессе, чтобы он был полезен как проект.

Моя следующая цель для процессора EightThirtyTwo-сделать бэкэнд VBCC достаточно полным, чтобы я мог скомпилировать и запустить тест Dhrystone. У меня есть только самое смутное представление о том, какой будет производительность, но я ожидаю, что она будет где-то между 30 и 50 DMIPS на поток при запуске из блока RAM. [Редакция: по состоянию на 2019-11-29 у меня еще нет бенчмарка, но, глядя на код, который я получаю от бэкэнда C, я подозреваю, что оценка очень амбициозна. Я считаю, что процессор способен работать в этом поле, но, вероятно, только при ручном кодировании на ассемблере!)


Адрес для контактов : imax9@narod.ru

Если вам понравились мои работы и вы желаете поддержать сайт - сделайте дотацию.

При копировании статьи – обязательна ссылка на авторство и источник. Без разрешения автора копирование запрещено.

© Максим Ильин 2022г.

Яндекс.Метрика