我刚刚获得了重新编写以下C函数的任务,以帮助ARM编译器生成更有效的汇编代码。有人知道该怎么做吗?
void some_function(int *data)
{
int i, j;
for (i = 0; i < 64; i++)
{
for (j = 0; j < 64; j++)
data[j + 64*i] = (i + j)/2;
}
}
首先(如乔纳森·莱夫勒所说,编译器可能已经做得很好,以至于试图通过编写特定的C代码来进行优化通常在商业上有问题,即,与通过稍微快一点的代码所能赚到的相比,您在开发时间上损失的钱更多。 。但是有时候这是值得的;让我们假设是这种情况。
如果您感到乐观,请在测量时这样做。编写最终不太理想的代码是很有可能的,因为以微妙的方式,否则可能的编译器优化就被挫败了。另外,是否进行优化以及进行多少优化取决于环境,即必须在所有潜在环境中进行测量。
好吧,经过那次明智的破解之后,我在其中的代码中演示了注释中建议的优化,其中之一是乔纳森·莱弗勒(Jonathan Leffler:]]
/* Jonathan Leffler */
void some_function(int *data)
{
int i, j;
int k = 0;
for (i = 0; i < 64; i++)
{
for (j = 0; j < 64; j++)
{
data[k++] = (i + j)/2;
}
}
}
/* Yunnosch 1, loop unrolling by 2 */
void some_function(int *data)
{
int i, j;
for (i = 0; i < 64; i++)
{
for (j = 0; j < 64; j+=2)
data[j + 64*i] = (i + j )/2;
data[j + 1 + 64*i] = (i + j+1)/2;
}
}
/* Yunnosch 1 and Jonathan Leffler */
void some_function(int *data)
{
int i, j;
int k=0; /* Jonathan Leffler */
for (i = 0; i < 64; i++)
{
for (j = 0; j < 64; j+=2) /* Yunnosch */
{
data[k++] = (i + j )/2;
data[k++] = (i + j+1)/2; /* Yunnosch */
}
}
}
/* Yunnosch 2, avoiding the /2, including Jonathan Leffler */
/* Well, duh. This is harder than I thought...
I admit that this is NOT tested, I want to demonstrate the idea.
Everybody feel free to help the very grateful me with fixing errors. */
void some_function(int *data)
{
int i, j;
int k=0;
for (i = 0; i < 32; i++) /* magic numbers I normally avoid, 32 is 64/2 */
{
for (j = 0; j < 32; j++)
{
data[k ] = (i + j);
data[k+1 ] = (i + j);
data[k +64] = (i + j);
data[k+1+64] = (i + j +1);
k+=2;
}
k+=64;
}
}
最新版本是基于以下可观察到的2x2组模式,具有预期的结果,如2D解释所示:
00 11 ...
01 12 ...
11 22 ...
12 23 ...
.. ..
.. ..
.. ..
´´´´
优化C代码以为特定的编译器/处理器生成“更有效的汇编代码”是您通常不应该做的事情。编写清晰易懂的C代码,并让编译器进行优化。
即使您使用C代码进行了各种技巧,并最终为特定的编译器/处理器获得了“更高效的汇编代码”,事实证明,简单的编译器升级可能会破坏整个过程,您将再次更改C代码。
对于像您的代码一样简单的内容,请从头开始用汇编代码编写。但是请注意,您必须是该处理器/汇编语言的真正专家才能击败像样的编译器。
无论如何...如果我们想猜测,这是我的猜测:
void some_function(int *data)
{
int i, j, x;
for (i = 0; i < 64; i++)
{
// Handle even i-values
x = i/2;
for (j = 0; j < 64; j += 2)
{
*data = x;
++data;
*data = x;
++data;
++x; // Increment after writing to data twice
}
++i;
// Handle odd i-values
x = i/2;
for (j = 0; j < 64; j += 2)
{
*data = x;
++data;
++x; // Increment after writing to data once
*data = x;
++data;
}
}
}
这个想法是1)用指针增量替换数组索引,以及2)用增量替换(i+j)/2
。
我已经没有做过任何测量,所以不能肯定地说这将是一个很好的解决方案。我将其留给OP。
与上述相同,但还有一些调整
void some_function(int *data)
{
for (int i = 0; i < 32; i++)
{
// when i is even, the output is in matched pairs
int value = i;
for (int j = 0; j < 32; j++)
{
*data++ = value;
*data++ = value++;
}
// when i is odd, the output starts with a singleton
// followed by matched pairs, and ending with a singleton
value = i;
*data++ = value++;
for (int j = 0; j < 31; j++)
{
*data++ = value;
*data++ = value++;
}
*data++ = value;
}
}