【问题标题】:Clean code for transforming a Array of Structures(AoS) to Structure of Arrays (SoA)?将结构数组 (AoS) 转换为数组结构 (SoA) 的干净代码?
【发布时间】:2014-06-14 19:39:35
【问题描述】:

我正在尝试将我的数据结构从结构数组更改为数组结构,似乎我正在为此编写大量冗余代码。(我想保留这两个代码。因为其中一个将在 GPU 上运行- Cuda 和其他在 CPU 上)

例如我有

struct constant_node_AoS {
    int id;
    double Zmiddle;
    double Height;
    int Bstatus;
    double gamma;
    double e;
    double cc;
    double cs;
    double spv0;
    double spvp;
} nodes_constprop; //actually it is more like nodes_constprop[nNodes];

所以我所做的是: 我创建了一个新结构

struct constant_node_struct_SoA {
    int *id;
    double *Zmiddle;
    double *Height;
    int *Bstatus;
    double *gamma;
    double *e;
    double *cc;
    double *cs;
    double *spv0;
    double *spvp;
}nodes_constprop_; 

那么我必须有这样的代码来分配内存

nodes_constprop_.id =  new int[nNodes]
nodes_constprop_.Zmiddle =  new double[nNodes]
nodes_constprop_.Height=  new double[nNodes]
.
.
.

还有一个用于移动数据的 for 循环

for (int i=0;i<nNodes;i++){
   nodes_constprop_.id[i] =  nodes_constprop[i].id;
   nodes_constprop_.Zmiddle[i] =  nodes_constprop[i].Zmiddle;
   nodes_constprop_.Height[i] =  nodes_constprop[i].Height;
   .
   .
   .
}

还有另一个用于释放数据的乱码!

所以我想知道也许有一些宏观技巧,或者我不知道其他一些巧妙的想法可以绕过这个烂摊子?

例如,有一个指向结构成员指针及其大小的指针数组,然后 for 循环进入它们并分配它们并移动数据?或者一些可以很好地扩展的解决方案,所以如果我添加多个其他成员,它会自动(或以最小的努力)得到照顾?

例如,有没有办法知道一个结构有多少成员(在编译时 - 例如这里应该是 10)所以用它来知道我的指针数组的大小?

谢谢

【问题讨论】:

  • 这实际上是CC++CUDA C/C++?您的标签可能需要修改。
  • 看来您的代码可能是 C++,而不是 C。('new' 不是 C)。或许您应该考虑添加“c++”并删除“c”标签?
  • 好吧,我没有在我的程序的任何部分使用类。它更多的是 C。但我认为我有使用 new 分配内存的习惯。所以我真的不知道是C还是C++。我主要是混合它们!
  • 你不会错过任何东西:结构数组很不方便。除非你追求一些非常具体的东西,否则我建议不要这样做。
  • new int 是 C 中的语法错误,因此如果您的代码编译成功,则说明您没有使用 C。CUDA GPU 语言是 C++ 的子集。

标签: c++ arrays struct cuda


【解决方案1】:

一个宏技巧看起来像这样。 在文件 struct.def 中放

NODE( int, id )
NODE( double, Zmiddle )
NODE( double, Height )
NODE( int, Bstatus )
NODE( double, gamma )
NODE( double, e )
NODE( double, cc )
NODE( double, cs )
NODE( double, spv0 )
NODE( double, spvp )

在 struct.h 文件中放

#ifdef NODE_AOS
struct constant_node_AoS {
#define NODE(a,b) a b ;
#include "struct.def"
} nodes_constprop[ nNodes ];

#define NODE_ID(i) nodes_constprop[ (i) ].id 
#define NODE_HEIGHT(i) nodes_constprop[ (i) ].Height
// repeat for rest of fields
#endif

#ifdef NODE_SOA
#define NODE(a,b) a nodes_ ## b[ nNodes ]  ;
#include "struct.def"

#define NODE_ID(i) nodes_id[ (i) ]
#define NODE_HEIGHT(i) nodes_Height[ (i) ]
// repeat for rest of fields
#endif

您的代码使用 NODE_ID(i) 来访问 id。

【讨论】:

  • 谢谢。有什么方法可以使用 NODE(j,i) 之类的东西吗?所以 j=1 -> ID, j=2 -> height,.... 像这样?所以我可以在 for 循环中使用它并设置所有成员
  • @RoozbehG 所有字段都必须是相同的类型才能工作。
【解决方案2】:

我的建议是创建几个函数:一个将struct 的数组转换为struct 的数组,另一个则相反。

为了说明,让我将structs 简化为:

struct constant_node_AoS {
    int id;
    double Zmiddle;
};

struct constant_node_struct_SoA {
    int *id;
    double *Zmiddle;
};

第一个函数是:

constant_node_struct_SoA translatetoSoA(constant_node_AoS* array, size_t size)
{
   constant_node_struct_SoA soa;
   soa.id = new int[size];
   soa.Zmiddle = new double[size];
   for ( size_t i = 0; i < size; ++i )
   {
      soa.id[i] = array[i].id;
      soa.Zmiddle[i] = array[i].Zmiddle;
   }

   return soa;
}

第二个功能是:

constant_node_AoS* translateToAoS(constant_node_struct_SoA soa, size_t size)
{
   constant_node_AoS* array = new constant_node_AoS[size];
   for (size_t i = 0; i < size; ++i )
   {
      arra[i].id = soa.id[i];
      array[i].Zmiddle = soa.Zmiddle[i];
   }

   return array;
}

【讨论】:

    【解决方案3】:

    由于您的结构成员名称相同,您可以使用@brian 的示例定义节点,然后使用宏访问节点

    #ifdef PROPS_AOS
    #define PROPNAME(var, name) (var).name
    #else
    #define PROPNAME(var, name) *(var).name
    #endif
    

    【讨论】:

    • 或者,您可以将两者都作为指针访问,AOS PROPNAME 将是 &amp;(var).name,非 AOS PROPNAME 将是 (var).name
    【解决方案4】:

    好吧,我对我的问题的回答:D

    我使用了下标运算符重载。

    struct constant_node_AoS {
        union {int id;double id_double;}
        double Zmiddle;
        double Height;
        union {int Bstatus;double Bstatus_double;}
        double gamma;
        double e;
        double cc;
        double cs;
        double spv0;
        double spvp;
        double &operator[](int i){
        switch (i){
          case 0:
            return id_double;
          case 1:
            return Zmiddle;
          case 2:
            return Height;
          case 3:
            return Bstatus_double;
          case 4:
            return gamma;
          case 5:
            return e;
          case 6:
            return cc;
          case 7:
            return cs;
          case 8:
            return spv0;
          case 9:
            return spvp;
        }
    
    } nodes_constprop[nNodes];
    

    其他结构也类似!

    struct constant_node_struct_SoA {
        union {long long *id;double *id_double}
        double *Zmiddle;
        double *Height;
        union {long long *Bstatus;double *Bstatus_double}
        double *gamma;
        double *e;
        double *cc;
        double *cs;
        double *spv0;
        double *spvp;
        double* &operator[](int i){
        switch (i){
          case 0:
            return id_double;
          case 1:
            return Zmiddle;
          case 2:
            return Height;
          case 3:
            return Bstatus_double;
          case 4:
            return gamma;
          case 5:
            return e;
          case 6:
            return cc;
          case 7:
            return cs;
          case 8:
            return spv0;
          case 9:
            return spvp;
        }
    }nodes_constprop_;
    

    所以现在我可以轻松拥有它了

    for (int i=0;i<10;i++)
        g_.nodes_constprop_[i] = new double[nNodes];
    
    for (int i=0;i<nNodes;i++)
        for (int j=0;j<10;j++)
            (g_.nodes_constprop_[j])[i] = (g_tmp->nodes_constprop[i])[j];
    

    我添加 union 的原因是没有它我无法找出类型转换错误(尽管我无论如何都应该添加 __align(8) 到它。 另外 long long 的原因也是我所做的,对 int 的指针操作是 4 个字节。我添加了以下代码

    typedef int int_align_double __attribute__ ((aligned(sizeof(double))));
    typedef int_align_double* pint_align_double;
    

    但 pint_align_double 类型的变量仍然是 4 字节指针。所以我把它改成了long long,这样我就可以得到8字节的指针对齐了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-08
      • 2014-01-16
      • 2015-06-22
      • 1970-01-01
      • 2019-04-04
      • 1970-01-01
      • 2014-11-13
      • 2018-04-19
      相关资源
      最近更新 更多