Драматическое падение скорости для доступа к статическому табуляции по кешу С++
Фон
Я смотрел потенциально используя статическое пространство RAM MPC5200 в виде памяти с нуля. У нас есть 16Kb неиспользуемой памяти, которая появляется на процессорной шине ( source).
Теперь некоторые важные примечания к реализации:
-
Эта память используется контроллером BestComm DMA, в RTEMS
это будет по существу настраивать таблицу задач в начале SRAM с набором из 16 задач, которые могут выполняться как буферы для периферийного интерфейса, I2C, Ethernet и т.д. Чтобы использовать это пространство без конфликтов и зная, что наша система использует только около 2 килобайт буферов драйвера Ethernet, я компенсировал начало SRAM на 8 КБ, поэтому теперь у нас есть 8 Кб памяти, которые, как мы знаем, не будут использоваться система.
-
RTEMS
определяет массив, который указывает на статическую память следующим образом:
(источник)
typedef struct {
...
...
volatile uint8_t sram[0x4000];
} mpc5200_t;
extern volatile mpc5200_t mpc5200;
И я знаю, что массив sram указывает на статическую память, потому что когда я редактирую первый раздел и распечатываю блок памяти (MBAR + 0x8000
source)
Итак, я могу сказать следующее: у меня есть определенный RTEMS доступ к SRAM
через mpc5200.sram[0 -> 0x2000]
. Это означает, что я могу начать делать некоторые тесты на скорости, которую я могу получить.
Test
Чтобы оценить скорость, я установил следующий тест:
int a; // Global that is separate from the test.
**TEST**
// Set up the data.
const unsigned int listSize = 0x1000;
uint8_t data1[listSize];
for (int k = 0; k < listSize; ++k) {
data1[k] = k;
mpc5200.sram[k] = k;
}
// Test 1, data on regular stack.
clock_t start = clock();
for (int x = 0; x < 5000; ++x) {
for (int y = 0; y < 0x2000; ++y) {
a = (data1[y]);
}
}
double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;
printf("elapsed dynamic: %f\n" ,elapsedTime);
// Test 2, get data from the static memory.
start = clock();
for (int x = 0; x < 5000; ++x) {
for (int y = 0; y < 0x2000; ++y) {
a = (mpc5200.sram[y]);
}
}
elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;
printf("elapsed static: %f\n" ,elapsedTime);
Довольно просто, концепция состоит в том, что мы итерируем доступное пространство и устанавливаем глобальное. Мы должны ожидать, что статическая память должна иметь одинаковое приблизительное время.
РЕЗУЛЬТАТ
Итак, мы получаем следующее:
elapsedDynamic = 1.415
elapsedStatic = 6.348
Итак, здесь что-то происходит, потому что статичность почти в 6 раз медленнее кэша.
Гипотезы
Итак, у меня было 3 идеи о том, почему это:
- Кэш пропускает, я подумал, может быть, тот факт, что мы смешиваем динамический и статический баран, что происходит что-то странное. Поэтому я пробовал этот тест:
.
// Some pointers to use as incrementers
uint8_t *i = reinterpret_cast<uint8_t*>(0xF0000000+0x8000+0x1000+1);
uint8_t *j = reinterpret_cast<uint8_t*>(0xF0000000+0x8000+0x1000+2);
uint8_t *b = reinterpret_cast<uint8_t*>(0xF0000000+0x8000+0x1000+3);
// I replaced all of the potential memory accesses with the static ram
// variables. That way the tests have no interaction in terms of
// memory locations.
start = clock();
// Test 2, get data from the static memory.
for ((*i) = 0; (*i) < 240; ++(*i)) {
for ((*j) = 0; (*j) < 240; ++(*j)) {
(*b) = (mpc5200.sram[(*j)]);
}
}
elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;
printf("elapsed static: %f\n" ,elapsedTime);
Имеются следующие результаты:
elapsedDynamic = 0.0010
elapsedStatic = 0.2010
Итак, теперь он в 200 раз медленнее? Так что, я думаю, это не связано с этим?
-
Статическая память отличается от обычной. Следующее, что я подумал, это то, что, возможно, она не взаимодействует, как я думал, что из-за этой строки:
MPC5200 содержит 16 Кбайт встроенной SRAM. Эта память напрямую доступна блоку BestComm DMA. Он используется в основном как хранилище для таблицы задач и дескрипторы буфера, используемые BestComm DMA для перемещения периферийных данных в SDRAM или из других мест. Эти дескрипторы должны быть загружены в SRAM при загрузке. Эта SRAM находится в внутреннем регистровом пространстве MPC5200 и также доступна для ядра процессора. Как таковой он может быть использован для других целей, таких как хранение царапин. SRAM 16 кбайт начинается с местоположения MBAR + 0x8000.
(источник)
Я не уверен, как подтвердить или опровергнуть это?
- Медленные статические часы. Возможно, статическая память работает на более медленных часах, например, в некоторых системах?
Это можно опровергнуть, посмотрев в руководстве:
![enter image description here]()
(источник)
SRAM и процессор были на одних и тех же часах, XLB_CLK
работает на основной частоте процессора (источник)
Вопрос
Что может быть причиной этого, есть ли причины вообще не использовать SRAM для хранения сменных площадок? Я знаю, что на современных процессорах это даже не рассматривается, но это старый встроенный процессор, и мы борются за скорость и пространство.
ДОПОЛНИТЕЛЬНЫЕ ИСПЫТАНИЯ
Итак, после комментариев ниже я провел несколько дополнительных тестов:
- Добавить
volatile
в член стека, чтобы узнать, являются ли скорости более равными:
.
elapsedDynamic = 0.98
elapsedStatic = 5.97
Итак, все еще гораздо быстрее и на самом деле не меняется с изменчивым?
- Разберите код, чтобы увидеть, что происходит
.
// original code
int a = 0;
uint8_t data5[0x2000];
void assemblyFunction(void) {
int * test = (int*) 0xF0008000;
mpc5200.sram[0] = a;
data5[0] = a;
test[0] = a;
}
void assemblyFunction(void) {
// I think this is to load up A
0: 3d 20 00 00 lis r9,0
8: 80 09 00 00 lwz r0,0(r9)
14: 54 0a 06 3e clrlwi r10,r0,24
mpc5200.sram[0] = a;
1c: 3d 60 00 00 lis r11,0
20: 39 6b 00 00 addi r11,r11,0
28: 3d 6b 00 01 addis r11,r11,1 // Where do these come from?
2c: 99 4b 80 00 stb r10,-32768(r11)
test[0] = a;
c: 3d 20 f0 00 lis r9,-4096 // This should be the same as above??
10: 61 29 80 00 ori r9,r9,32768
24: 90 09 00 00 stw r0,0(r9)
data5[0] = a;
4: 3d 60 00 00 lis r11,0
18: 99 4b 00 00 stb r10,0(r11)
Я не очень хорошо разбираюсь в ассемблере, но, возможно, у нас есть проблема здесь? Доступ и установка памяти из глобальной системы, похоже, требуют больше инструкций для SRAM
?
- Из вышеприведенного теста кажется, что для указателя меньше инструкций, поэтому я добавил следующее:
.
uint8_t *p = (uint8_t*)0xF0008000;
// Test 3, get data from static with direct pointer.
for (int x = 0; x < 5000; ++x) {
for (int y = 0; y < 0x2000; ++y) {
a = (p[y]);
}
}
И я получаю следующий результат:
elapsed dynamic: 0.952750
elapsed static: 5.160250
elapsed pointer: 5.642125
Таким образом, указатель принимает EVEN LONGER! Я бы подумал, что это будет точно так же? Это просто становится странным.
Ответы
Ответ 1
Итак, похоже, есть несколько факторов, которые могут привести к этому.
- Я не уверен, что SRAM работает с той же тактовой частотой, что и процессор. Как указано в @user58697,
SRAM
находится на часах времени IPB, даже если это похоже на то, что шина находится на XLB времени. Кроме того, есть диаграмма:
(источник)
Это, по-видимому, указывает на то, что часы памяти находятся на пути XLB, но путь XLB находится на более низкой частоте, чем часы CORE. Это можно подтвердить здесь:
![enter image description here]()
(источник)
Что означает, что XLB_Bus работает медленнее, чем процессор.
- Чтобы проверить, что SRAM, по крайней мере, быстрее, чем динамический ram, я провел следующее тестирование:
.
// Fill up the cache with pointless stuff
for (int i = 0; i < 4097; ++i) {
a = (int)TSin[i];
}
// 1. Test the dynamic RAM access with a cache miss every time.
ticks = timer_now();
// += 16 to ensure a cache line miss.
for (int y = 0; y < listSize; y += 16) {
a = (data1[y]);
}
elapsedTicks = timer_now() - ticks;
// Fill up the cache with pointless stuff again ...
ticks = timer_now();
// Test 2, do the same cycles but with static memory.
for (int y = 0; y < listSize; y += 16) {
a = (mpc5200.sram[y]);
}
elapsedTicks = timer_now() - ticks;
И при этом мы получаем следующие результаты:
elapsed dynamic: 294.84 uS
elapsed static: 57.78 uS
Итак, что мы можем сказать здесь, это то, что статическая ОЗУ быстрее, чем динамическая RAM (ожидаемая), но когда динамическая RAM загружается в кеш, обращаясь к статическому плунжеру, намного медленнее, поскольку доступ к кешу находится на скорости процессора, а статическая скорость штока намного меньше.