Драматическое падение скорости для доступа к статическому табуляции по кешу С++

Фон

Я смотрел потенциально используя статическое пространство 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 + 0x8000source)

Итак, я могу сказать следующее: у меня есть определенный 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 раз медленнее? Так что, я думаю, это не связано с этим?

  1. Статическая память отличается от обычной. Следующее, что я подумал, это то, что, возможно, она не взаимодействует, как я думал, что из-за этой строки:

    MPC5200 содержит 16 Кбайт встроенной SRAM. Эта память напрямую доступна блоку BestComm DMA. Он используется в основном как хранилище для таблицы задач и дескрипторы буфера, используемые BestComm DMA для перемещения периферийных данных в SDRAM или из других мест. Эти дескрипторы должны быть загружены в SRAM при загрузке. Эта SRAM находится в внутреннем регистровом пространстве MPC5200 и также доступна для ядра процессора. Как таковой он может быть использован для других целей, таких как хранение царапин. SRAM 16 кбайт начинается с местоположения MBAR + 0x8000.

(источник)

Я не уверен, как подтвердить или опровергнуть это?

  1. Медленные статические часы. Возможно, статическая память работает на более медленных часах, например, в некоторых системах?

Это можно опровергнуть, посмотрев в руководстве:

enter image description here

(источник)

SRAM и процессор были на одних и тех же часах, XLB_CLK работает на основной частоте процессора (источник)


Вопрос

Что может быть причиной этого, есть ли причины вообще не использовать SRAM для хранения сменных площадок? Я знаю, что на современных процессорах это даже не рассматривается, но это старый встроенный процессор, и мы борются за скорость и пространство.


ДОПОЛНИТЕЛЬНЫЕ ИСПЫТАНИЯ

Итак, после комментариев ниже я провел несколько дополнительных тестов:

  • Добавить volatile в член стека, чтобы узнать, являются ли скорости более равными:

.

elapsedDynamic = 0.98
elapsedStatic = 5.97

Итак, все еще гораздо быстрее и на самом деле не меняется с изменчивым?

  1. Разберите код, чтобы увидеть, что происходит

.

// 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?

  1. Из вышеприведенного теста кажется, что для указателя меньше инструкций, поэтому я добавил следующее:

.

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 времени. Кроме того, есть диаграмма:

enter image description here (источник)

Это, по-видимому, указывает на то, что часы памяти находятся на пути XLB, но путь XLB находится на более низкой частоте, чем часы CORE. Это можно подтвердить здесь:

enter image description here

(источник)

Что означает, что XLB_Bus работает медленнее, чем процессор.

  1. Чтобы проверить, что 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 загружается в кеш, обращаясь к статическому плунжеру, намного медленнее, поскольку доступ к кешу находится на скорости процессора, а статическая скорость штока намного меньше.