• Windows+GCC下内存对齐的常见问题


    结构/类对齐的声明方式

    gcc和windows对于modifier/attribute的支持其实是差不多的。比如在gcc的例子中,内存对齐要写成:

    class X
    {
      //...
    } __attribute__((aligned(16)));

    但是实际上你写成

    class __attribute__((aligned(16))) X 
    {
        /*...*/
    };

    gcc一样可以识别。这样MSVC和gcc就可以使用宏完成跨平台编译。

    对齐类型的变量在堆与栈上的分配

    对齐在以下场合都能提示编译器为它的变量分配对齐的地址:

    void foo()
    {
        X v; // v是个栈上的16字节对齐的变量
        X* p = new X; // p是堆上的16字节对齐的指针
        X* a = new X[ARRAY_SIZE]; // 那么这个呢?
    }

    栈上的变量堆上分配出的变量,因为align这个hint的存在,都能满足16字节对齐的要求。但是数组呢?按照一般规律来分析,对齐后的sizeof(X),一定是对齐的整数倍。比如16字节对齐的话,那么X的大小只能是16的倍数。所以对于本例的数组而言,编译器应该也能知道a应该是16字节对齐的。

    但是事实上挺奇怪。在MSVC上,p和a都很好的遵守了对齐的要求;在gcc上,p是对齐的,但是a却不是。其实这个问题在2004年便有人提出来,只是到目前为止一直都没有人动手过。当然,标准也没有规定X的数组就一定是要对齐的。要解决这个问题,要么重载class的operator new/delete,要么用memalign/aligned_malloc分配出对齐的内存,再placement new。出于易用性,我选择的是操作符重载。

    clang对于对齐的支持更干脆:16B的对齐已经够用了。所以align完全被编译器忽视了。结果Intel出来了AVX,Clang就傻逼了。不知道这个问题3.4会不会修正。

    编译器如何实现内存对齐

    MSVC在x86下默认是支持的4B的内存对齐。也就是说在函数入口处,ESP和EBP只保证是4字节对齐的。这时,当前函数域栈上变量的地址都是ESP + 4 * x的形式。如果函数体内有对齐的变量,例如:

    void foo()
    {
        int __declspec(align(16)) x;
        // ...
    }

    那么编译器在代码生成时,会在函数的前部插入一段称为prolog的代码,这段代码会将堆栈修正为16B对齐,比如

    PUSH EBP
    MOV  EBP, ESP
    SUB  ESP, XXX
    AND  ESP, 0xFFFFFFF0h

    这样ESP就一定是16字节对齐的。这个时候给x分配的地址,就可以是ESP + 0x10 * n的形式,这样就满足了对齐的需要。

    在GCC上,gcc认为所有的函数都有义务在调用其它函数的时候,ESP是16字节对齐的(当然,可以通过编译选项修改这一要求)。不光是调用方会这样保证,被调用方也是这样默认的。所以GCC为了调用效率更高一点,便根据调用方的假设,去掉了“堆栈修正”这个步骤。

    原来的代码可能就变成了

    PUSH EBP             ; 假设这里的ESP是16B对齐的,Push了EBP,ESP就是16x-4了。
    MOV  EBP, ESP
    SUB  ESP, 0x0000023Ch ; 减完以后这里又是16字节对齐了

    那么当被调用方遵守这个约定的时候,ESP当然就是16字节对齐的。但是有一种情况例外。在MinGW下,线程的入口函数是被API回调的。这个函数很可能是按照Windows的标准4个字节对齐的。这样,在没有堆栈修正的情况下,整个线程调用链16B对齐的默契就被打破了。如果这个时候出现了SSE代码试图存取“16字节对齐”的变量,那可能就会发生segment fault的异常,因为这些变量的地址并不是对齐的。

    解决这个问题,有两种常见的办法:第一,写一个Wrapper函数,对齐ESP后转发调用;第二,使用编译选项-mstackrealign。这个选项会为所有函数增加堆栈修正的PROLOG代码,以保证函数栈帧一定是按照16字节或用户指定大小对齐。

  • 相关阅读:
    设计模式六大原则
    .net Stream篇(七)
    .net Stream篇(六)
    .net Stream篇(五)
    .net Stream篇(四)
    Leetcode 18
    Leetcode 16
    Leetcode 15
    Leetcode 12
    Leetcode 9
  • 原文地址:https://www.cnblogs.com/lingjingqiu/p/3446457.html
Copyright © 2020-2023  润新知