1

简而言之,以下哪个可能是更好的选择:

somestruct data[lots];
for(int i=0;i<lots;i++) {
    a(&data[i]);
    b(&data[i]);
    c(&data[i]);
}
//....
void a(somestruct* d) { ..stuff with d.. }
void b(somestruct* d) { ..stuff with d.. }
void c(somestruct* d) { ..stuff with d.. }

或者

somestruct data[lots];
a(lots,data);
b(lots,data);
c(lots,data);
//...
void a(int n, somestruct* d) {
    for(int i<n;i++) {
        ..stuff with d[i]..
} }
void b(int n, somestruct* d) {
    for(int i<n;i++) {
        ..stuff with d[i]..
} }
void c(int n, somestruct* d) {
    for(int i<n;i++) {
        ..stuff with d[i]..
} }

我的理解是,对于 A,当前活动的结构将被缓存,从而进行改进,但会有一大堆函数调用(否定)。另一方面,对于 B,我将丢弃我的缓存,但有 3 个函数调用,而不是 3*lots 函数调用。

如果我的编译器决定内联 a、b 和 c,那么第一个选项应该是最好的选择(因为我们现在拥有两全其美的选择),但是我的感觉是,在大多数情况下,如果没有,那么函数调用远高于内存访问。

我知道唯一知道的方法是对我的特定应用程序进行基准测试,但很好奇我是否在这里遗漏了任何经验法则。第一个版本为我的具体情况生成了一些更简洁的代码,但差别不大。

编辑

我试图问一个关于输入函数的成本(分配堆栈指针和其他任何东西)与缓存未命中的成本相比如何的问题。我认为一个通用的答案也可能对其他人有用。但是,问题的一般形式似乎无法令人满意地回答,所以这里是完整的统计数据,假设我计算正确:

  • data 是 100,000 个结构,每个结构包含六个双精度和一个 int。
  • a() 是 6 次双读、3 次双乘、3 次双加和 3 次双存储。
  • b() 是三个双读、三个双乘、一个四加、两个双比较,以及有条件地调用 c。
  • c() 是三个双倍乘法、三个函数调用和三个双写。
4

1 回答 1

0

您应该问的主要问题是函数调用和内存访问(尤其是缓存未命中)之间的区别是什么。如果数据结构很大(大到缓存无法容纳),就会出现一些缓存未命中的情况。在您提供的第一种情况下,这些缓存未命中将重复 3 次,而在第一种情况下,它们只会发生一次。此外,函数可以按每 10 个元素的顺序访问数据结构。这将增加缓存未命中的数量。如果数据结构足够小,就不会有缓存未命中,并且函数调用开销将接管(如果编译器没有内联它们)。但是,一般来说,你不能假设要分析的数据结构的大小,所以我会选择第一个解决方案,保持问题的普遍性。

于 2013-10-19T20:46:45.797 回答