【问题标题】:Which way of arranging fields in structure is better?在结构中排列字段的哪种方式更好?
【发布时间】:2023-03-25 19:44:02
【问题描述】:

我有两种方法来排列结构中的字段

  struct foo {  
    float   a;
    double  b;
    float   c;
    double  d;
    short   e;
    long    f;
    short   g;
    long    h;
    char    i;
    int     j;
    char    k;
    int     l;
  };




  struct foo {  
    double  b;
    double  d;
    long    f;
    long    h;
    float   a;
    float   c;
    int     j;
    int     l;
    short   e;
    short   g;
    char    i;
    char    k;
  };

应该练习哪一个,为什么?

【问题讨论】:

  • 这听起来像家庭作业。我会抛出一个词:填充。我猜你可以弄清楚其余的。
  • A4L,这不是严格意义上的风格/意见问题,因为由于填充,顺序实际上会影响结构所需的总内存和执行速度。不过很大程度上取决于所使用的编译器和标志,并且在大多数情况下影响不大。
  • @A4L 谢谢,以后会记住的!
  • 只需为每个变体执行printf("%d\n", sizeof(struct foo)),您就会知道哪个更好,以及为什么...
  • @a4l,我很确定在您选择的语言(似乎是 Java)中同样的问题适用。只是在大多数语言(包括 Java)中,您对程序的看法是如此高级,而您对性能的关注如此之少,以至于您从未听到过有关此类事情的讨论。然而,从处理器的角度来看,填充对缓存的影响和对齐对内存访问的影响与编程语言完全无关。

标签: c performance structure


【解决方案1】:

排列结构成员的最佳方法是按其大小将它们按升序排列。 Doing this, you reduce the paddingdata alignment 需要。

另外,使用一些preprocessor directives 可以修改对齐方式,例如:

#define packed_data __attribute__((__packed__))

将其关闭。使用它,成员顺序不再重要,但我强烈反对这样做,因为内存对齐已针对性能进行了优化。

【讨论】:

  • 按大小排序没关系,只要按大小排序即可。增加大小的排序会将填充分配到不同的部分末端,减少大小的排序将形成一个实心数据块,并在末尾应用所有填充。填充量是一样的,只是分布不一样。
  • @JonasWielicki 我明白了,你是对的。排序是我逃脱的原因,我很乐意放弃评论。非常感谢您非常指出这一点。非常感谢。
  • @cmaster Jonas 刚刚指出了这一点,我很抱歉没有看到这一点(这里仍然是早上 6 点 30 分,咖啡最多也很少)。我很乐意放弃不准确的评论。感谢你们俩的健全性检查。顺便说一句,对评论 +1。
  • 我本来打算举一个反例,但结果是this
【解决方案2】:

第二种安排更可取。

在结构体中,尽量减少因填充造成的位存储损失

(内存中各种变量的边界对齐),最好是开始

具有最大变量的结构。从大到小。

  1. 查看结构中的填充

  2. C 中变量类型的大小

    (http://www.cquestions.com/2011/02/size-of-data-types-in-c.html)

【讨论】:

    【解决方案3】:

    排序字段的一种明智方式是根据它们的目的相似性。 例如:

    struct window {
        MENU   *menu ;
        int    width ;
        char   *name ;
        WINDOW *child ;
        int    xpos ;
        int    height ;
        void   *info ;
        WINDOW *next ;
        CLASS  *class ;
        int    ypos ;
        WINDOW *parent ;
        }
    

    会更好看

    struct window {
        CLASS  *class ;
        void   *info ;
        char   *name ;
        int    xpos ;
        int    ypos ;
        int    width ;
        int    height ;
        WINDOW *parent ;
        WINDOW *next ;
        WINDOW *child ;
        MENU   *menu ;
        }
    

    【讨论】:

      【解决方案4】:

      填充是偏爱一种排列而不是另一种排列的原因之一。另一种选择是按字段的频率或内存访问模式对字段进行排序。这当然是微优化,但它可以在极端情况下获得回报。

      许多或大多数 CPU 架构将较小的偏移量编码为具有较小操作码的指针;在 RISC 架构中,将最常访问的元素置于偏移量 0 可以减少指令数量,并使代码运行得更快。

      如果“x”与“y”同时使用,将它们放在同一个缓存行中是有益的,而不是面临每个结构两个或多个缓存未命中的风险。

      【讨论】:

      • 至少在我所知道的 RISC 架构中,与 CISC 架构相比,它们实际上不太关心偏移量大小或偏移量为零之类的事情。原因是 RISC 架构具有固定的命令大小(通常为 32 位),其中可以打包相当多的信息。不使用这些位只会浪费宝贵的空间。例如,在 Power 平台上,有 0 到 32 范围内的五个(!)参数的命令。同样,您没有任何简化版本的命令,如加载和存储。所有内存访问都允许偏移,这很简单。
      • 如果RISC意味着固定指令长度,那么根据定义。 Cortus APS3 例如具有 16/32 位指令,但由于 os 加载/存储体系结构和 固定指令吞吐量,非常类似于 RISC。也省略了ARM实际上属于哪个类别的问题,它具有相似的属性。
      猜你喜欢
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-30
      • 1970-01-01
      • 2018-10-06
      • 1970-01-01
      相关资源
      最近更新 更多