我有以下结构:
struct Matrix
{
int numOfRows;
int numOfColumns;
double* values;
int* permutationVector;
}
我还有以下功能:
void SetRowToZero(Matrix* m, int row)
{
int rowBegin = row*(m->numOfColumns);
for (int c = 0; c < (m->numOfColumns); c++)
{
m->values[rowBegin + c] = 0;
}
}
我想知道在操作c < (m->numOfColumns)
期间是否有任何性能下降?
如果我写这样的函数有什么区别:
void SetRowToZero(Matrix* m, int row)
{
// Unpacking structure
int numOfColumns = m->numOfColumns;
double* values = m->values;
int rowBegin = row*(m->numOfColumns);
for (int c = 0; c < numOfColumns; c++)
{
values[rowBegin + c] = 0;
}
}
总的来说,我是否应该关注如此小规模的表演?
答案 0 :(得分:4)
应该没有性能问题可以谈论:优化编译器应该能够将您的第一个代码片段转换为第二个代码片段,将指针存储在寄存器中,优化索引c
,或者使用寻址模式,用于计算硬件中的offset+index
。
注意:如果您更喜欢指针运算,则可以在不使用索引的情况下重写循环。这段代码的性能类似于原始的两个代码块,但它几乎直接转换为一个简单的汇编代码块,指针存储在寄存器中:
void SetRowToZero(Matrix* m, int row) {
double *rowPtr = m->values + (row*(m->numOfColumns));
double *pastEndPtr = rowPtr + m->numOfColumns;
while (rowPtr != pastEndPtr) {
*rowPtr++ = 0;
}
}
另请注意,所有三个实现都不是线程安全的:如果m->numOfColumns
或m->values
的值在另一个线程的循环中间发生更改,那么最终会出现以下行为:很可能是未定义的,而且绝对是意外的。
答案 1 :(得分:4)
编译器优化将导致优化两段代码以使用该值的寄存器。 我在x86_64上测试了这个小程序,并使用gcc 5.4进行了4级优化编译
#include <stdlib.h>
struct Matrix
{
int numOfRows;
int numOfColumns;
double* values;
int* permutationVector;
};
void SetRowToZero1(struct Matrix* m, int row)
{
int rowBegin = row*(m->numOfColumns);
for (int c = 0; c < (m->numOfColumns); c++)
{
m->values[rowBegin + c] = 0;
}
}
void SetRowToZero2(struct Matrix* m, int row)
{
// Unpacking structure
int numOfColumns = m->numOfColumns;
double* values = m->values;
int rowBegin = row*(m->numOfColumns);
for (int c = 0; c < numOfColumns; c++)
{
values[rowBegin + c] = 0;
}
}
int main() {
struct Matrix matrix = {5,1000000, malloc(5 * 1000000 * sizeof(double)), NULL};
SetRowToZero1(&matrix, 1);
}
我编译了它:
gcc -O4 main.c -o test1.out
然后我改变了
SetRowToZero1(&matrix, 1);
到
SetRowToZero2(&matrix, 1);
编译:
gcc -O4 main.c -o test2.out
然后: $ md5sum test1.out test2.out
504fb75e97173a6864750f5feb7cea58 test12.out
504fb75e97173a6864750f5feb7cea58 test1.out
所以你可以肯定地说实现没有区别:)
答案 2 :(得分:3)
Maxim:可读性优于性能。
换句话说,计算机很便宜,而程序员却不便。
在这种情况下没有区别,因为循环条件是一个简单的成员访问。
如果循环条件是一个函数,则可能是另一回事。但在这种情况下,您可以向后运行循环:
for (int c = <expensive function> - 1 c >= 0; --c)
{
m->values[rowBegin + c] = 0;
}
如果c
是unsigned
类型,请格外小心。
答案 3 :(得分:2)
这两段代码没有区别。他们做同样的事情,一个使用中间变量。
如果从可读性的角度来看,使用临时工具是有意义的,那么就使用它们,否则不要使用它们。