cortex-m7 trm, раздел руководства пользователя по кэш-памяти.
В безошибочной системе основное влияние на производительность оказывает стоимость
схема чтения-изменения-записи для неполных хранилищ на стороне данных. Если
слот буфера хранения не содержит хотя бы полного 32-битного слова, он
необходимо прочитать слово, чтобы иметь возможность вычислить контрольные биты. Это может
происходит, потому что программное обеспечение записывает только в область памяти с байтом или
инструкции по хранению полуслов Затем данные могут быть записаны в ОЗУ.
Это дополнительное чтение может оказать негативное влияние на производительность, потому что
предотвращает использование слота для другой записи.
.
Буферизация и выдающиеся возможности маски памяти системы
часть дополнительного чтения, и она незначительна для большинства кодов.
Тем не менее, ARM рекомендует использовать как можно меньше кэшируемых STRB и STRH.
инструкции, насколько это возможно, чтобы уменьшить влияние на производительность.
У меня есть cortex-m7s, но на сегодняшний день я не провел тест, чтобы продемонстрировать это.
Что означает «прочитать слово», это чтение одной ячейки памяти в SRAM, которая является частью кэша данных. Это не системная память высокого уровня.
Внутренние части кеша построены из блоков SRAM и вокруг них, которые являются быстрой SRAM, которая делает кеш тем, что он есть, быстрее системной памяти, быстро возвращает ответы обратно процессору и т. Д. Это чтение-изменение-запись (RMW) не является политикой записи высокого уровня. Они говорят, что если есть попадание и политика записи говорит, что нужно сохранить запись в кеше, тогда байт или полуслово должны быть записаны в одну из этих SRAM. Ширина данных SRAM кэша данных с ECC, как показано в этом документе, составляет 32 + 7 бит. 32 бита данных 7 битов контрольных битов ECC. Вы должны держать все 39 бит вместе, чтобы ECC работал. По определению вы не можете изменить только некоторые биты, так как это приведет к ошибке ECC.
Всякий раз, когда необходимо изменить любое количество бит в этом 32-битном слове, хранящемся в данных SRAM кэша данных, 8, 16 или 32 бита, необходимо пересчитать 7 контрольных битов и записать все 39 битов одновременно. Для 8- или 16-битной записи STRB или STRH необходимо прочитать 32 бита данных, изменив 8 или 16 битов, оставив биты данных в этом слове неизменными, 7 проверенных битов ECC и 39 битов, записанных в sram .
В идеале вычисление контрольных битов должно происходить в одном и том же тактовом цикле, который устанавливает запись, но чтение и запись не находятся в одном и том же тактовом цикле, поэтому для записи поступивших данных должно потребоваться как минимум два отдельных цикла. в кеше за один такт. Существуют приемы, чтобы задержать запись, которая иногда также может повредить, но обычно перемещает ее в цикл, который был бы неиспользован и делает его свободным, если хотите. Но это не будет тот же тактовый цикл, что и чтение.
Они говорят, что если вы будете держать язык за зубами и сумеете получить достаточно небольших хранилищ, попадете в кеш достаточно быстро, они остановят процессор, пока не смогут его догнать.
В документе также описывается, что SRAM без ECC имеет ширину 32 бита, что означает, что это также верно при компиляции ядра без поддержки ECC. У меня нет доступа ни к сигналам для этого интерфейса памяти, ни к документации, поэтому я не могу сказать наверняка, но если он реализован как 32-битный интерфейс без управления байтовой дорожкой, то у вас возникает та же проблема, он может записать только 32-битный элемент к этой SRAM, а не к дробным частям, поэтому для замены 8 или 16 битов необходимо использовать RMW в недрах кэша.
Короткий ответ, почему бы не использовать более узкую память, - это размер чипа, а для ECC размер удваивается, поскольку существует ограничение на количество проверочных битов, которые можно использовать даже при уменьшении ширины (7 бит на каждые 8 бит гораздо больше битов, чтобы сохранить, чем 7 бит на каждые 32). Чем уже память, тем больше у вас сигналов для маршрутизации и вы не можете упаковать память настолько плотно. Квартира против группы отдельных домов, чтобы вместить одинаковое количество людей. Дороги и тротуары к входной двери вместо прихожих.
И особенно с таким одноядерным процессором, если только вы не попытаетесь преднамеренно (что я и сделаю), вряд ли вы случайно столкнетесь с этим и зачем повышать стоимость продукта: возможно, этого не произойдет?
Обратите внимание, что даже с многоядерным процессором вы увидите память, созданную следующим образом.
EDIT.
Хорошо, дошли до теста.
0800007c <lwtest>:
800007c: b430 push {r4, r5}
800007e: 6814 ldr r4, [r2, #0]
08000080 <lwloop>:
8000080: 6803 ldr r3, [r0, #0]
8000082: 6803 ldr r3, [r0, #0]
8000084: 6803 ldr r3, [r0, #0]
8000086: 6803 ldr r3, [r0, #0]
8000088: 6803 ldr r3, [r0, #0]
800008a: 6803 ldr r3, [r0, #0]
800008c: 6803 ldr r3, [r0, #0]
800008e: 6803 ldr r3, [r0, #0]
8000090: 6803 ldr r3, [r0, #0]
8000092: 6803 ldr r3, [r0, #0]
8000094: 6803 ldr r3, [r0, #0]
8000096: 6803 ldr r3, [r0, #0]
8000098: 6803 ldr r3, [r0, #0]
800009a: 6803 ldr r3, [r0, #0]
800009c: 6803 ldr r3, [r0, #0]
800009e: 6803 ldr r3, [r0, #0]
80000a0: 3901 subs r1, #1
80000a2: d1ed bne.n 8000080 <lwloop>
80000a4: 6815 ldr r5, [r2, #0]
80000a6: 1b60 subs r0, r4, r5
80000a8: bc30 pop {r4, r5}
80000aa: 4770 bx lr
есть версии загрузочного слова (ldr), загрузочного байта (ldrb), сохраненного слова (str) и сохраненного байта (strb), каждая из которых выровнена по крайней мере на 16-байтовых границах до вершины адреса цикла .
с включенным icache и dcache
ra=lwtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=lwtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=lbtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=lbtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=sbtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=sbtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
0001000B
00010007
0001000B
00010007
0001000C
00010007
0002FFFD
0002FFFD
нагрузки находятся на одном уровне, как и ожидалось, однако магазины, когда вы собираете их таким образом, записывают байты в 3 раза дольше, чем запись слова.
но если не ударить по кешу так тяжело
0800019c <nbtest>:
800019c: b430 push {r4, r5}
800019e: 6814 ldr r4, [r2, #0]
080001a0 <nbloop>:
80001a0: 7003 strb r3, [r0, #0]
80001a2: 46c0 nop ; (mov r8, r8)
80001a4: 46c0 nop ; (mov r8, r8)
80001a6: 46c0 nop ; (mov r8, r8)
80001a8: 7003 strb r3, [r0, #0]
80001aa: 46c0 nop ; (mov r8, r8)
80001ac: 46c0 nop ; (mov r8, r8)
80001ae: 46c0 nop ; (mov r8, r8)
80001b0: 7003 strb r3, [r0, #0]
80001b2: 46c0 nop ; (mov r8, r8)
80001b4: 46c0 nop ; (mov r8, r8)
80001b6: 46c0 nop ; (mov r8, r8)
80001b8: 7003 strb r3, [r0, #0]
80001ba: 46c0 nop ; (mov r8, r8)
80001bc: 46c0 nop ; (mov r8, r8)
80001be: 46c0 nop ; (mov r8, r8)
80001c0: 3901 subs r1, #1
80001c2: d1ed bne.n 80001a0 <nbloop>
80001c4: 6815 ldr r5, [r2, #0]
80001c6: 1b60 subs r0, r4, r5
80001c8: bc30 pop {r4, r5}
80001ca: 4770 bx lr
тогда слово и байт занимают одинаковое количество времени
ra=nwtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=nwtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=nbtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
ra=nbtest(0x20002000,0x1000,STK_CVR); hexstring(ra%0x00FFFFFF);
0000C00B
0000C007
0000C00B
0000C007
байт по-прежнему занимает в 4 раза больше времени, чем слова, все остальные факторы остаются постоянными, но это было проблемой, когда байты занимают более чем в 4 раза больше времени.
так что, как я описывал перед этим вопросом, вы увидите, что размер кэш-памяти является оптимальной шириной в кэше, а также в других местах и при записи байтов будет происходить чтение-изменение-запись. Теперь, является ли это видимым для других издержек или оптимизаций или нет, это отдельная история. ARM четко заявило, что это может быть видно, и я чувствую, что продемонстрировал это. Это ни в коей мере не отрицательно относится к дизайну ARM, на самом деле, наоборот, RISC в целом переходит наверх по мере выполнения инструкций / выполнения, для выполнения той же задачи требуется больше инструкций. Эффективность в дизайне позволяет таким вещам быть видимыми. Есть целые книги, написанные о том, как заставить ваш x86 работать быстрее, не выполнять 8-битные операции для того или другого, или другие инструкции предпочтительны, и т. Д. Это означает, что вы должны быть в состоянии написать тест для демонстрации этих падений производительности. Точно так же, как этот, даже если вы вычисляете каждый байт в строке, когда вы перемещаете его в память, это должно быть скрыто, вам нужно написать код, подобный этому, и если вы собираетесь делать что-то подобное, вы можете записать инструкции, объединяющие байты. в слово, прежде чем писать, может быть или не быть быстрее ... зависит.
Если бы у меня было половинное слово (strh), то неудивительно, что оно также переносит чтение-модификацию-запись, поскольку оперативная память имеет ширину 32 бита (плюс любые экси-биты, если есть)
0001000C str
00010007 str
0002FFFD strh
0002FFFD strh
0002FFFD strb
0002FFFD strb
нагрузки занимают столько же времени, сколько ширина sram считывается как целое и помещается на шину, процессор извлекает из этого интересующие байтовые дорожки, поэтому для этого не нужно тратить время / часы.