用数组或指针引用函数?

Pereferring function with array or pointer?

本文关键字:函数 引用 指针 数组      更新时间:2023-10-16

我有两个函数,其中一个是带有数组的函数,另一个是带有指针参数的函数。它们返回相同的结果,但我不知道哪个更好用。

#define SIZE 1024
int sumA(int a[SIZE][SIZE])
{
int sum = 0;
for(int y = 0; y < SIZE; y++)
for(int x = 0; x < SIZE; x++)
sum += a[x][y];
return sum;
}
int sumB(int *a)
{
int sum[4] = {0, 0, 0, 0};
for( int i = 0; i < SIZE*SIZE; i += 4 )
{
sum[0] += a[i+0];
sum[1] += a[i+1];
sum[2] += a[i+2];
sum[3] += a[i+3];
}
return sum[0] + sum[1] + sum[2] + sum[3];
}

在这两种情况下,您都是通过引用传递数组,因此没有区别。

您的 sum 函数显然对传入的数组有很多了解。所以我认为最好强制数组是函数所期望的那种。

编辑:如果将 int[][] 类型的变量传入接受 int 指针 (int*( 的函数中,则必须将该变量显式转换为 int*,否则编译器将不接受它。

因此,福尔

int sumA(int a[SIZE][SIZE])

是两者中更好的。

从性能的角度来看,sumB更好,因为您正在循环展开。要注意的是a是否是您不检查的 4 的倍数,这可能会导致程序崩溃。

编辑:sumA当然更好,因为它更严格(它知道确切的尺寸(

第一种解决方案对类型更安全,但对性能不利:

  1. 循环是按每个字节在内存中添加跳转的顺序,这对缓存不利
  2. 你必须依靠编译器展开循环,甚至意识到它可以由一个而不是两个来完成

第二种解决方案是手动循环展开,使用分布式和变量可以很好地与 cpu 流水线配合使用(如果一开始没有矢量化(并减少分支。

两者都被矢量化,但第二个更好。 在第一个中,替换循环顺序确实可以改善事情,但不会使生成的程序集相等(虽然它们的速度接近,但第二个要长几倍(。 https://godbolt.org/g/bW1Jkd 我测量了 10 倍的性能差异(大肠杆菌上有 -O3,gcc 上(支持第二种解决方案(。

因此,我建议将它们两者混合在一起:

int sumA(int a[SIZE][SIZE])
{
static_assert(SIZE % 4 == 0);
int* flat_a = &(a[0][0]);
int sum[4] = {0, 0, 0, 0};
for( int i = 0; i < SIZE*SIZE; i += 4 )
{
sum[0] += flat_a[i+0];
sum[1] += flat_a[i+1];
sum[2] += flat_a[i+2];
sum[3] += flat_a[i+3];
}
return sum[0] + sum[1] + sum[2] + sum[3];
}

这不是一个复杂的功能,一切仍然易于阅读。

此外,我不认为4常数应该成为"非魔法",除非展开是完全通用的,但这需要一些模板魔法。 命名一个值应该表明它可以在不完全破坏所有内容的情况下进行更改。

在 2 之间,我将使用更多类型,但使用正确的命名sumB可能是可行且更通用的:int sumSIZESIZEints(const int*)

与您的预期相反,忽略sumA的一个SIZE,导致

int sumA(int (*a)[SIZE])

更类型的是:

int sum(const int (&a)[SIZE][SIZE])
{
return std::accumulate(&a[0][0], &a[0][0] + SIZE * SIZE, 0);
}