【发布时间】:2014-02-16 12:04:49
【问题描述】:
我正在考虑大量内存缓存优化,并希望得到一些反馈。 考虑这个例子:
class example
{
float phase1;
float phaseInc;
float factor;
public:
void process(float* buffer,unsigned int iSamples)//<-high prio audio thread
{
for(unsigned int i = 0; i < iSamples; i++)// mostly iSamples is 32
{
phase1 += phaseInc;
float f1 = sinf(phase1);//<-sinf is just an example!
buffer[i] = f1*factor;
}
}
};
优化思路:
void example::process(float* buffer,unsigned int iSamples)
{
float stackMemory[3];// should fit in L1
memcpy(stackMemory,&phase1,sizeof(float)*3);// get all memory at once
for(unsigned int i = 0; i < iSamples; i++)
{
stackMemory[0] += stackMemory[1];
float f1 = sinf(stackMemory[0]);
buffer[i] = f1*stackMemory[2];
}
memcpy(&phase1,stackMemory,sizeof(float)*1);// write back only changed mameory
}
请注意,真正的示例循环将包含数千个操作。 所以 stackMemory 可以变得相当大。 我认为它不会超过 32kb(那里有更小的 L1 吗?)。
这个堆栈内存中使用的变量的顺序重要吗? 我希望不会,因为我想订购它们,这样我就可以减少回写大小。 还是 L1 缓存具有与 RAM 相同的缓存线行为?
我感觉我在做预取的用途,但我所读到的关于预取的所有内容都相对模糊,关于如何有效地使用它。尝试和错误不是 5000 多行代码的选项。
代码将在 Win、Mac 和 iOS 上运行。 任何 ARMIntel 问题?
这种优化有没有可能是无用的,因为所有内存都在循环的第一次迭代中被访问并转移到 L1 ?
感谢任何提示和想法。
【问题讨论】:
-
你是否建议优化3个成员变量的mem访问?无论如何,它们都会被一起获取(并且可能在同一访问中),并在此过程中填充到缓存中。您只是创建了一个额外的副本,这会占用缓存上的额外空间(假设编译器无法消除这种不必要的重复)。
-
尝试将
phase1 += phaseInc;移出循环(编译器可能已经这样做了)。它变成了phase1 += phaseInc*iSamples; -
对我来说这似乎是过早的优化。即使您在循环中调用的唯一函数是
sinf,在循环的一次迭代中它也会比其他任何函数都更昂贵。然后你写了“注意,真正的示例循环将包含数千个操作。” 这进一步证实了你正在优化错误的东西。减少循环内的工作并忘记如果你想让你的循环更快,关于缓存。 -
这个例子应该只是展示我打算如何优化。真正的循环将包含数千行(已经优化的)代码。不幸的是,在音频 DSP 世界中,您有很多数据依赖性,因此不可能将代码从循环中取出。
标签: c++ performance caching optimization