首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C语言中用于3D循环的宏

C语言中用于3D循环的宏
EN

Stack Overflow用户
提问于 2014-08-05 13:11:12
回答 3查看 114关注 0票数 2

我正在开发一个C (C99)程序,它在许多地方对3-D数组进行大量循环。因此,下面的访问模式在代码中是普遍存在的:

代码语言:javascript
复制
for (int i=0; i<i_size, i++) {
    for (int j=0; j<j_size, j++) {
        for (int k=0; k<k_size, k++) {
            ...
        }
    }
}

自然,这会使许多代码行变得混乱,并需要大量的复制。所以我想知道使用宏来使它更紧凑是否有意义,就像这样:

代码语言:javascript
复制
#define BEGIN_LOOP_3D(i,j,k,i_size,j_size,k_size) \
for (int i=0; i<(i_size), i++) { \
    for (int j=0; j<(j_size), j++) { \
        for (int k=0; k<(k_size), k++) { 

代码语言:javascript
复制
#define END_LOOP_3D }}}

一方面,从DRY principle的角度来看,这似乎很棒:它使代码更加紧凑,并允许您将循环的内容缩进一个块而不是三个块。另一方面,引入新语言构造的做法看起来丑陋得可怕,尽管我现在想不出有任何明显的问题,但似乎很容易产生令人担忧的调试噩梦的bug。

那么你是怎么想的:尽管丑陋和潜在的缺点,但紧凑和减少重复是否证明这是合理的?

EN

回答 3

Stack Overflow用户

发布于 2014-08-05 13:55:00

千万不要在宏中放入open或close {}。C程序员不习惯这样做,因此代码变得难以阅读。

在您的情况下,这甚至是完全多余的,您只是不需要它们。如果你做了这样的事情,那就做吧

代码语言:javascript
复制
FOR3D(I, J, K, ISIZE, JSIZE, KSIZE)      \
for (size_t I=0; I<ISIIZE, I++)          \
    for (size_t J=0; J<JSIZE, J++)       \
        for (size_t K=0; K<KSIZE, K++)

不需要终止宏。程序员可以直接放置{}

此外,上面我使用了size_t作为C中循环索引的正确类型。3D矩阵很容易变大,当你不去想它的时候,int算法就会溢出。

票数 1
EN

Stack Overflow用户

发布于 2014-08-05 14:45:41

最好的方法是使用函数。让编译器担心性能和优化,但是如果你关心的话,你总是可以将函数声明为内联函数。

下面是一个简单的例子:

代码语言:javascript
复制
#include <stdio.h>
#include <stdint.h>

typedef void(*func_t)(int* item_ptr);


void traverse_3D (size_t  x, 
                  size_t  y, 
                  size_t  z, 
                  int     array[x][y][z],
                  func_t  function)
{
  for(size_t ix=0; ix<x; ix++)
  {
    for(size_t iy=0; iy<y; iy++)
    {
      for(size_t iz=0; iz<z; iz++)
      {
        function(&array[ix][iy][iz]);
      }
    }
  }
}


void fill_up (int* item_ptr)  // fill array with some random numbers
{
  static uint8_t counter = 0;
  *item_ptr = counter;
  counter++;
}

void print (int* item_ptr)
{
  printf("%d ", *item_ptr);
}


int main()
{
  int arr [2][3][4];

  traverse_3D(2, 3, 4, arr, fill_up);
  traverse_3D(2, 3, 4, arr, print);
}

编辑

为了停止所有的猜测,这里有一些来自Windows的基准测试结果。测试是用大小为2040的矩阵完成的。可以从traverse_3D调用fill_up函数,也可以直接从main()中的3级嵌套循环调用该函数。使用QueryPerformanceCounter()进行基准测试。

案例1: gcc -std=c99 -pedantic-errors -Wall

代码语言:javascript
复制
With function, time in us:      255.371402
Without function, time in us:   254.465830

案例2:-Wall -O2的gcc -std=c99 -pedantic-errors

代码语言:javascript
复制
With function, time in us:      115.913261
Without function, time in us:   48.599049

案例3: gcc内联-std=c99 - -Wall -errors -O2,traverse_3D函数

代码语言:javascript
复制
With function, time in us:      37.732181
Without function, time in us:   37.430324

为什么“没有函数”的情况在内联函数的情况下表现得更好,我不知道。我可以注释掉对它的调用,仍然可以得到“无函数”情况下的相同基准测试结果。

然而,结论是,通过适当的优化,性能很可能不是问题。

票数 0
EN

Stack Overflow用户

发布于 2014-08-05 16:15:51

如果这些3D数组很“小”,你可以忽略我。如果你的3D数组很大,但是你不太关心性能,你可以忽略我。如果你赞同(常见但错误的)原则,即编译器是准魔法工具,几乎可以在不考虑输入的情况下产生最佳代码,那么你可以忽略我。

您可能知道有关宏的一般警告,它们如何阻碍调试等,但如果您的3D数组是“大”的(无论这意味着什么),并且您的算法是面向性能的,那么您的策略可能存在您可能没有考虑到的缺点。

首先:如果你在做线性代数,你几乎肯定想要使用专用的线性代数库,比如BLAS,LAPACK等,而不是“滚动你自己的”。OpenBLAS (来自GotoBLAS)将完全抽掉你编写的任何等效代码,可能至少是一个数量级。如果你的矩阵是稀疏的,这是双重正确的;如果你的矩阵是稀疏的和结构化的(比如三对角线),这是三重正确的。

其次:如果您的3D数组表示用于某种模拟(如有限差分法)的笛卡尔网格,并且/或者打算将其提供给任何数值库,那么您绝对不希望将它们表示为C3D数组。相反,您将希望使用1D C数组,并在可能的情况下使用库函数,并在必要的情况下自己执行索引计算(有关详细信息,请参阅this answer )。

第三:如果您确实必须编写自己的三重嵌套循环,则循环的嵌套顺序是一个重要的性能考虑因素。ijk顺序(而不是ikj或kji)的数据访问模式很可能会导致算法的缓存行为不佳,例如,密集矩阵-矩阵乘法就是这种情况。您的编译器也许能够进行一些有限的循环交换(据我所知,icc可以为naive xGEMM生成相当快的代码,但gcc不会)。随着您实现越来越多的三重嵌套循环,您提出的解决方案变得越来越有吸引力,“一个循环顺序适合所有人”策略在所有情况下都能提供合理性能的可能性变得越来越小。

第四:任何在每个维度的全范围内迭代的“一个循环-顺序适合所有人”的策略都不会被平铺,并且可能会表现出较差的性能。

第五(参考另一个我不同意的答案):一般来说,我认为对于任何对象来说,“最好的”数据类型是具有最小大小和最少代数结构的集合,但是如果您决定放纵内心,使用size_t或另一个无符号整数类型作为矩阵索引,您会后悔的。1994年,我用C++编写了我的第一个朴素线性代数库。在过去的8年里,我用C语言写了大约半打,每次,我都开始尝试使用无符号整数,但每次,我都会后悔。我最终决定size_t是用来表示事物的大小的,而矩阵索引并不是任何事物的大小。

第六(参考另一个我不同意的答案):对于深度嵌套的循环,HPC的一个基本规则是避免在最内层的循环中调用函数和分支。在最内层循环中的操作计数很小的情况下,这一点尤其重要。如果您正在执行一些操作,通常情况下,您不希望在其中添加函数调用开销。如果你在那里做成百上千的操作,你可能不关心函数调用/返回的几条指令,因此,它们是可以的。

最后,如果以上这些都不符合您试图实现的内容,那么您提出的建议没有错,但我会仔细考虑Jens关于花括号所说的话。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25131574

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档