指针在c程序中的循环

时间:2017-11-17 16:21:13

标签: c performance optimization reference

我有以下结构:

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;
    }
}

总的来说,我是否应该关注如此小规模的表演?

4 个答案:

答案 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->numOfColumnsm->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;
}

如果cunsigned类型,请格外小心。

答案 3 :(得分:2)

这两段代码没有区别。他们做同样的事情,一个使用中间变量。

如果从可读性的角度来看,使用临时工具是有意义的,那么就使用它们,否则不要使用它们。