Обновить
27
0.1
Виктор@byman

Пользователь

Отправить сообщение

Когда-то программировал под ARM и там как раз такое решение - располагать константы в дырках между функциями. Но в данном случае компилятор не пользуется таким приемом, а кладет константу только за кодом.

Да. Код у меня расположен , начиная с 0-го адреса. Секция данных сразу за кодом. Размер кода при -О0 1244 байта, при -О2 1246 байт. Я специально для -О2 вырезал часть кода из ассемблерного стартапа , но компилятор не поменял решения.

LUI+ADDI это типовая склейка в одну команду у RISC-V. Вы правы в предположении.Загрузка константы в простом суперскалере на 2 команды за такт займет максимум 4 такта. Иногда компилятор делает LUI+ADDI не перемешивая. А в конце сдвиг и сложение. Теоретически сдвиг на 32 и сложение тоже можно склеить. Не знаю делают ли так, но в этом случае 3 такта.

Чтобы понять какой вариант быстрее нужен не только тест, но еще нужно выбирать и конкретную реализацию процессора. У меня такое впечатление , что компилятор отлаживают на каком-то доступном процессоре и под него все оптимизируется. Опять же Вы упомянули про кэш , а это тоже особенности реализации.

Компилятор ведет себя по-разному в зависимости от ситуации. Пример ld_K_a0(0xFEDCBA9876543210);. При оптимизации -О0 компилятор дает код

 174:	4b003783          	ld	a5,1200(zero)
 178:	853e                	mv	a0,a5
 17a:	f69ff0ef          	jal	e2 <ld_K_a0>

При оптимизации -О2

188:	fedcc7b7          	lui	a5,0xfedcc
 18c:	a9878793          	addi	a5,a5,-1384 # fffffffffedcba98
 190:	76543537          	lui	a0,0x76543
 194:	1782                slli	a5,a5,0x20
 196:	21050513          	addi	a0,a0,528 # 76543210
 19a:	953e                add	a0,a0,a5
 19c:	f47ff0ef          	jal	e2 <ld_K_a0>

Обратите внимание, что одиночное использование константы дает накладные расходы минимум в 3 слова при загрузке из памяти. В случае встраивания константы в код максимум 5 слов. Если загрузка константы потребует полного формирования адреса команды (AUIPC+ADDI) , то это будут те же 5 слов при загрузке из памяти. Остается вопрос - почему при -О2 якобы более плохой код? Может это лучшее решение ? :)

приведите пример "трудной" константы. Я напрягу компилятор.

Посмотрел что дает компилятор для константы в статье

lui a5,0x12345

addi a5,a5,1657 # 12345679

lui a0,0x90abd

slli a5,a5,0x20

addi a0,a0,-529 # ffffffff90abcdef

add a0,a0,a5

Похоже я немного попутал.. Это у Вас пример программы и к загрузке имеет отношение только ld t0, pc + (value-start) В этом случае будет что-то похожее на код

auipc t0,0x0
addi t0,t0,26 # 110 <K_val>
ld t0,0(t0)

У Вас вызов и возврат, и неизвестно сколько придется на этом потерять. "Намного быстрее" хорошо бы привести на каком-то примере.

Посмотрите здесь https://textarchive.ru/c-2726176.html

Получается, что АРВ всегда начинает с L2. Но еще нужно знать стратегию по записи у L1. Возможно она сквозная.

Спасибо. Да, 5 тактов из рисунка 6. Но в новом документе видно что есть два варианта: приблизительно 5 и 10 тактов. 5 если через кэш L2. Если без него, то хуже получается. Рисунки 2 и 3 дают 3 такта из памяти L2. Получается 16/3=5.3 байта на такт. Из Ваших цифр ранее я считал так 4+7=11 на 64 байта. Итого 64/11 = 5.8 Практически одинаково.

Просто у меня доступ к полной статье запрещен :) А статья полезная.

НАшел интересную ссылку https://en.eeworld.com.cn/news/qrs/eic272697.html

Ваш тест в большей степени как раз тест на пропускную способность памяти. Шаг адреса равен 16. Если я не ошибаюсь, то получается скорость 5 тактов на чтение из любой память (префетч для SL2 + DDR). Итого 16/5 = 3.2 байта на такт.

Похоже что 16 байт за такт можно получить только когда 32К байт все сидят в кэше L1D. Если чуть больше данных, то далее все должно определяться задержкой подкачки линии из Prefetchera в L1D и тем, как шустро Prefetche будет подкачивать следующий пакет.

Подкачка работает только при обращении к внешней памяти (в XMC). 256 бит для L1D нарисовано потому, что 2х64 по чтению и 2х64 по записи. Ранее Вы написали задержку загрузки линии кэша.

Банки L1D это всего лишь расслоение по 8-ми словам. Ядро каждый такт будет читать 4 из них. IDMA будет писать 2 из них. Пересечение и конфликт неминуем. Правда, если тормозить будет процессор, а не IDMA, то на общее времени исполнения этого пинг-понга конфликты не должны повлиять.

При добавлении 2-го юнита в коде добавятся еще 4 инструкции и все это влезет в один пакет. Адрес в B12 = A12+8 и инкремент будет на 16 байт. Вроде так? 11 тактов на 64 байта это 5.8 байта на такт из памяти L2. Частный случай. У DSP обычно все красиво когда данные внутри какой-то удобной памяти. "Маленький" обьем для Эльбруса у Вас 7 млн байт :) .

Это не очень хороший прием. Трафик у IDMA в 2 раза ниже чем у ядра. Да еще к той же памяти, с которой работает ядро. Т.е. еще и с ядром будет конфликт доступа. Здесь (для сравнения) все лучше делать аналогично Эльбрусу: кэш L1 максимальный 32К, кэш L2 максимальный 512 К. Вместо кэша L3 внутреннюю 4М память.

Из Вашего замечания непонятно за кого бояться - что Эльбрус проиграет С66 или наоборот. Можете прояснить у кого на FFT нет шансов победить? :)

Да. С этим девайсом было бы интереснее посоревноваться :) Надеюсь, что для случая FFT у Вас будут не только цифры Эльбруса, но и цифры TI. Хотя бы для С66.

Да. Интерфейс к памяти узковат. Здесь еще очень интересен случай когда размеры массива превышают обьем кэша L1D..

Спасибо большое за такой развернутый ответ. Можно сделать вывод , что на С66 теоретически можно 16 байт за такт накапливать? При выравненных адресах загрузки.

1
23 ...

Информация

В рейтинге
3 959-й
Откуда
Беларусь
Зарегистрирован
Активность