优化(在C语言中):许多函​​数调用与一个函数调用

时间:2016-12-08 22:06:44

标签: c optimization function-calls

例如,我想创建一个将节点添加到列表的函数(insertNode)。每次我想要添加一个节点,或者将所有节点存储在一个数组中,并且通过将数组作为参数传递并调用insertNode一次,调用insertNode会更快吗?剩下的功能呢?

代码示例:

typedef struct Data {
    int *myArray;             //the array where all integers are stored
    int firstAvailablePos;    //the first free position of myArray
} Data;

insertNode(Data *data, int newNum) {
    (data->myArray)[data->firstAvailablePos] = newNum;
    (data->firstAvailablePos)++;
}

alt_insertNode(Data *data, int *array, int arraySize) {
    int i;
    for(i = 0; i < arraySize; i++)
         (data->myarray)[i] = array[i];
}

main中,两个选项是:

  1. 许多函数调用

    while (...) {
        ...
        insertNode(data, newNum);
    }
    
  2. 一个函数调用

    anArraySize = 0;
    while (...) {
        ...
        anArray[i] = newNum;
        anArraySize++;
        ...
    }
    alt_insertNode(data, anArray, anArraySize);
    

5 个答案:

答案 0 :(得分:1)

这取决于底层列表实现的结构。如果这是一个数组并且insertNode附加它,它将被复制到内存中的新位置,其中有更多可用空间,以便新元素也适合。新元素也获得memcpyd。这很快,因为它发生在内核而不是用户空间程序中。

另一方面,如果您有类似链接列表的东西,其中存储了以阵列形式存在的其他列表的指针。甚至不必复制列表中的所有元素,只需向链表插入一个新指针,指向包含新元素的数组。那会非常快。

我目前所知道的最佳答案是:取决于。

答案 1 :(得分:1)

函数调用比迭代更昂贵。如果通过调用insertNode函数插入节点,则需要进行一些迭代(取决于您要插入的位置)以在列表中插入该节点,但如果要插入大量节点,则每次都需要调用该函数。这可能是时间昂贵。

如果通过将某个节点放入数组来插入节点,最后调用insertNode将数组的节点复制到列表中。这次insertNode将被调用更少的时间,但是交互次数将增加。这不会是时间成本,就像函数调用一样。

有一件事需要注意,你需要额外的数据存储空间。

答案 2 :(得分:1)

您的代码存在问题:

  • 您使用过时的语法进行函数定义。现代C代码不再支持隐式返回类型,您应将其指定为void

  • 您使用无关的括号使代码看起来很尴尬。

  • 函数insertNode不会检查myArray指向的数组是否足够大。如果需要,您应该检查并重新分配数组。

  • 函数alt_insertNode不会检查可用空间,也不会更新firstAvailablePos

根据您的重新分配方案以及您的编译器允许优化的积极程度,批量插入值比逐个插入值可能更有效,特别是如果您不分配中间数组与malloc()。对特定测试用例进行基准测试将告诉您哪个更有效。但请注意,使代码尽可能简单具有重要价值。

以下是可用于运行测试的更完整的实现:

typedef struct Data {
    int *myArray;             // the array where all integers are stored
    size_t size;              // the number of int that can be stored
    size_t firstAvailablePos; // the first free position of myArray
} Data;

/* reallocating the array with a simple exponential growth */
int insertNode(Data *data, int newNum) {
    if (data->firstAvailablePos == data->size) {
        size_t newSize = (data->size < 32) ? 32 : data->size + data->size / 2;
        int *array = realloc(myArray, newSize * sizeof(*array));
        if (array == NULL)
            return -1;
        data->myArray = array;
        data->size = newSize;
    }
    data->myArray[data->firstAvailablePos++] = newNum;
    return 0;
}

int alt_insertNode(Data *data, int *array, size_t arraySize) {
    if (data->firstAvailablePos + arraySize > data->size) {
        size_t newSize = (data->size < 32) ? 32 : data->size + data->size / 2;
        while (newSize < data->firstAvailablePos + arraySize) {
            newSize += newSize / 2;
        }
        int *array = realloc(myArray, newSize * sizeof(*array));
        if (array == NULL)
            return -1;
        data->myArray = array;
        data->size = newSize;
    }
    memcpy(data->myArray + data->firstAvailablePos, array, arraySize * sizeof(*array));
    data->firstAvailablePos += arraySize;
    return 0;
}

答案 3 :(得分:0)

这取决于程序中的许多内容。具体来说,节点是按某种算法排序还是顺序不重要?如果是这样,传递数组节点或单独插入将没有太大区别(因为排序块或信息并将节点插入到排序位置应该是等效的。)

您可能还会考虑程序中的哪个点需要在列表中插入节点。如果你的列表使用了动态内存,那么有必要在需要的时候有一个函数在需要的时候插入一个节点。

答案 4 :(得分:0)

这取决于调用代码的外观,但一般来说,如果可以内联,则短函数调用将具有零开销。创建一个临时数组只是为了避免函数调用开销几乎肯定是一个错误 - 但在某些情况下&#34;批处理&#34;这样的东西可以避免一堆每个插入的开销,可能会感觉到。

尝试两者并进行基准测试。