为什么通过缓存 c++ 访问静态 ram 的速度会急剧下降?

Why is there a dramatic speed drop for access to static ram over cache c++?

背景

我一直在研究将 MPC5200 静态 ram space 用作 scratch pad memory. We have 16Kb of unused memory that appears on the processor bus (source 的可能性。

现在一些重要的实施说明是:

  1. 此内存由 BestComm DMA 控制器使用,在 RTEMS 下,这实际上会在 table 处设置一个任务以一组 16 个任务开始 SRAM,这些任务可以 运行 作为外设接口、I2C、以太网等的缓冲区。为了使用这个 space 而不会发生冲突,并且知道我们的系统只使用了大约 2Kb 的以太网驱动程序缓冲区,我将 SRAM 的起始偏移了 8Kb,所以现在我们有 8Kb 的内存,我们知道系统不会使用它。

  2. RTEMS定义了一个指向静态内存的数组,如下:

(source)

typedef struct {
    ...
    ...
    volatile uint8_t    sram[0x4000];
 } mpc5200_t;

 extern volatile mpc5200_t mpc5200;

而且我知道 sram 数组指向静态内存,因为当我编辑第一部分并打印出内存块时(MBAR + 0x8000 source )

所以从这里我可以说以下内容,我有 RTEMS 定义的通过 mpc5200.sram[0 -> 0x2000] 访问 SRAM 的权限。这意味着我可以开始对我可以摆脱它的速度进行一些测试。


测试

为了评估速度,我设置了以下测试:

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);    

很简单,概念是我们迭代可用的 space 并设置一个全局的。我们应该期望静态内存应该具有相同的近似时间。


结果

所以我们得到以下内容:

elapsedDynamic = 1.415
elapsedStatic = 6.348

所以这里发生了一些事情,因为静态比缓存慢了将近 6 倍。


假设

所以我有 3 个关于为什么会这样的想法:

  1. 缓存未命中,我想也许是因为我们正在混合动态和静态 ram,所以发生了一些奇怪的事情。所以我尝试了这个测试:

.

// 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 contains 16KBytes of on-chip SRAM. This memory is directly accessible by the BestComm DMA unit. It is used primarily as storage for task table and buffer descriptors used by BestComm DMA to move peripheral data to and from SDRAM or other locations. These descriptors must be downloaded to the SRAM at boot. This SRAM resides in the MPC5200 internal register space and is also accessible by the processor core. As such it can be used for other purposes, such as scratch pad storage. The 16kBytes SRAM starts at location MBAR + 0x8000.

(source)

我不确定如何确认或否认这一点?

  1. 较慢的静态时钟,也许静态内存运行在较慢的时钟上运行,就像在某些系统中一样?

这可以通过查看手册来反驳:

(source)

SRAM 和处理器在同一个时钟上,XLB_CLK 运行 处于处理器基频 (source)


问题

可能是什么原因造成的,通常有什么理由不使用 SRAM 进行暂存器存储吗?我知道在现代处理器上这甚至不会被考虑,但这是一个较旧的嵌入式处理器,我们正在努力争取速度和 space。


额外测试

所以在下面的评论之后我进行了一些额外的测试:

  1. 添加volatile到stack member,看速度是否更均等:

.

elapsedDynamic = 0.98
elapsedStatic = 5.97

所以仍然快得多,并且 volatile 没有任何变化??

  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

所以指针需要甚至更长!我还以为会一模一样?这只是越来越陌生。

看来有几个因素可能会导致这种情况。

  1. 我不再确定 SRAM 运行 的时钟速度与处理器相同。正如 @user58697 所指出的,SRAM 处于 IPB 时钟时间,即使看起来总线处于 XLB 时间。最重要的是这张图:

(source)

这似乎表明他的内存时钟在 XLB 路径上,但 XLB 路径的频率低于 CORE 时钟。这可以在这里确认:

(source)

这表明 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 快(预期)但是当动态 RAM 加载到缓存中时访问静态 RAM 慢得多,因为缓存访问是处理器速度并且静态 ram 速度远低于此。