【问题标题】:C Loop Optimization [closed]C循环优化[关闭]
【发布时间】:2014-06-02 13:28:04
【问题描述】:

你们知道如何优化这个循环吗?数组大小为 10,000。

int *inlink_num = calloc(npages, sizeof(int))
int **inlink_index = calloc(npages, sizeof(int *));
int *outlink_num = calloc(npages, sizeof(int));

//there is an function to initialize above three variable


for (int i = 0; i < npages; i++)
{   
    for (int j = 0; j < inlink_num[i]; j++)
    {
        sum += (store_val[inlink_index[i][j]] / (double) outlink_num[inlink_index[i][j]]);
    }

    rank_val[i] =  constant_part + dampener * sum;
    convergence = rank_val[i] - store_val[i];
    threshold += (convergence * convergence);
    sum = 0.0;
}

请帮忙!

【问题讨论】:

  • store_val 更改为双精度数组可能会有所帮助。
  • store_valinlink_indexoutlink_num 是如何声明的?
  • store_val 是如何定义的?
  • 将您的指针声明为restrict 可能会有很大帮助。
  • 使用指针(这是 C 中的一种迭代器);-)。然后: 优化取决于这里的大小: 如果 store_val 小于 inlink_index: 创建一个与 store_val 长度相同的数组 ratios 并首先计算 ratios = store_val / outlink_num。然后按上述方法对比率进行求和。

标签: c loops optimization


【解决方案1】:

通常的假设是内部循环是唯一值得优化的。不幸的是,我们似乎拥有的是 outer 循环的大小,为 10,000。

如果这种循环的性能很重要,可以在这里提出问题并花时间查看答案,那么完成前三件事也很重要。

  1. 基准。假设完成。
  2. 个人资料。找出哪些代码行是性能瓶颈。未完成。
  3. 检查生成的代码。查看编译器生成的内容。未完成。

因此,请编辑您的问题以提供此附加信息。

同时,有 3 种广泛的策略可以提高此类程序的性能。

  1. 更改算法。寻找不同的方法来计算结果和/或使用更多 CPU 内核。
  2. 帮助编译器。对代码进行细微的更改,以便编译器可以更好地优化。
  3. 更好地使用 CPU。改善缓存局部性和/或更好地选择指令,例如 SIMD。

更多的 CPU 内核意味着线程。不容易,但对于这种代码,速度可能会提高 4 倍。

类似 RESTRICT 的建议有助于编译器检测和避免别名。我的猜测是这段代码非常简单,编译器不需要帮助。在查看生成的代码之前,您不会知道。

循环中心的指针间接看起来肯定会破坏缓存。是否可以使用不同的数据结构?目标是一个适合 L2 缓存并具有正确对齐属性的平面多维数组。

这些实际上只是起点。总有办法让代码运行得更快。如果没有,那就买更多的硬件。

【讨论】:

    【解决方案2】:

    你是如何初始化inlink_index数组的,j下标的范围是多少?

    如果您正在查找 inlink_index 数组末尾的随机索引值,那么您的代码对超高速缓存不友好,预计会运行,不仅不正确,而且运行速度非常慢。

    如果 0..j-1 很大,并且您不关心元素的顺序,只是循环遍历所有元素,那么按索引值排列排序数据将提高缓存局部性。

    显示的代码看起来可能没有正确地从列表转换为数组:

    int **inlink_index = calloc(npages, sizeof(int *));
    ...
    for (int i = 0; i < npages; i++)
    {   
        for (int j = 0; j < inlink_num[i]; j++)
        {
            sum += (store_val[inlink_index[i][j]] / (double) outlink_num[inlink_index[i][j]]);
        }
    ...
    

    为 inlink_index 分配的字节数是 npages * (sizeof int *),所以可能是 npages * 2 * 4 字节(64 位指针)。现在,i 从 0.. npages-1 变化,但每个条目只有 sizeof int * bytes,而不是 0..max j-1。这怎么可能是正确的?

    如果你想用[i][j]下标成一个扁平的int数组,那么需要定义固定大小,所以a[i][j] = a + i*maxPossible(j) + j 有意义。因此,您会将 inlink_index 的大小设置为 npages * maxPossible(j) * sizeof int。但是如图所示,编译器如何计算出每一行的大小呢?

    另一方面,如果 j 是可变长度向量,存储整数,那么您需要显式取消引用 inlink_index[ i] 而不是将其视为平面数组:

    int inlink_row[] = inlink_index[ i];  /* Set row slice to base of int array */
    int index = inlink_row[ j];           /* Set int value */
    

    假设代码将 j 元素数组的地址分配给每个 inlink_index[i] 行。

    我认为,下标只是在做指针算术 *( *(inlink_index + i) + j) 这可能是 inlink_index + i * sizeof(int*) + j * sizeof(int) 或者 inlink_index + i * 8 + j * 4 bytes 并且没有做预期的指针取消引用。

    因为 char foo[2][4] 假设一个连续的内存分配,[0][0], [0][1] .. [0][3], [1][0], .. [ 1][3]。 char **foo; foo[0] = "one"; foo[1] = strdup ("two"); 在内存中没有连续的字符。

    【讨论】:

    • 我找到了问题所在。访问store_val[inlink_index[i][j]]时,太慢了。但是我该如何解决呢?
    • 为什么列表更快?这个 inlink_index 实际上是一个指针,因此构造时您不会从数组的隐式列表中受益,您知道“this”是 a[i],“next”是 a[i+1],“first”是 a[0 ]。您需要退后一步,研究整体问题并可能组织数据,以最大限度地提高性能。例如,如果 inlink_index 按值排序,则预取和缓存会很好地工作。
    • 按照目前的结构,您的问题没有提供足够的信息。
    猜你喜欢
    • 1970-01-01
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 2011-07-01
    • 1970-01-01
    • 2021-12-12
    相关资源
    最近更新 更多