首页 > 系统相关 >内存对齐

内存对齐

时间:2023-07-25 14:12:43浏览次数:39  
标签:字节 void 内存 对齐 alignment addr

一.内存对齐

内存对齐的好处

  • 某些情况下,可能需要多次访存,比如64位CPU,每次最多可获取8B,现在有一个8B长的变量,如果没有对齐,可能前6B存在前一个地址,后2B存在另一个地址,两次才能取出,如果对齐,那就只需要访存一次

  • 可能会破坏访存的原子性,常见的就是long long并发时的错误

    img

  • 某些ARM CPU不支持未对齐的内存访问(我没碰到过)

  • 可能会造成性能问题

    • 在 ARM v6/7 上未对齐的访问通常需要许多额外的周期才能完成
    • 在现代的 x86 处理器上,未对其的内存访问没有明显的性能损失。在这篇对 Intel SandyBridge 架构(酷睿 2xxx 系列,奔腾 G6xx 系列)的测试文章里提到there is noperformance penalty for reading or writing misaligned memory operands
    • 不仅如此,在这篇文章的测试中,在一些 workload 下,未对齐的内存访问甚至比对齐的访问更快!

二.平台内存对齐

64位平台,变量的地址最好是8B对齐的

三.结构体对齐

1.对齐规则

  1. 数据类型的对齐:结构体中的成员变量按照自身的大小进行对齐。例如,int 按照 4 字节对齐,double 按照 8 字节对齐,char 按照 1 字节对齐。
  2. 对齐边界:结构体的起始地址必须是其最大成员的对齐边界的倍数。换句话说,结构体的大小必须是最大成员大小的整数倍
  3. 填充字节:为了满足对齐要求,编译器可能会插入填充字节,使得成员变量正确对齐。

以下面几个结构体为例

案例1

struct test{    
 char   a;    
 int    b;    
 short  c;    
}test1;  

这个结构体中,最长的变量长度为4B

image-20230725114647825

案例2

struct test{    
 char   A;    
 short  C;   
 int    B;    
}test2;  
image-20230725114758537

为什么需要填充?

填充才能保证内存对齐

为什么结构体的大小必须是最大成员大小的整数倍?

这篇回答中,找到了一个可能的原因,以这个结构体为例

struct st{
    int32_t a;
    int8_t  b;
};
 
struct st arr[N];

如果不对齐为整数倍,那么占用5B,如果对齐,那就是8B,但是如果不填充,当对数组访问时,arr[1].a就会有4B放在arr[0].b中,导致cross line,从而对齐失败,

所以第二条实际上还是为了内存对齐,如果没有第二条,还是存在未对齐的隐患。

四.保证内存对齐的简单算法

#include <stddef.h>
#include <stdlib.h>

int posix_memalign(void **memptr, size_t alignment, size_t size) {
    if (alignment % sizeof(void*) != 0 || (alignment & (alignment - 1)) != 0) {
        // 对齐要求必须是void*大小的倍数,并且是2的幂次
        return EINVAL; // 参数错误
    }

    void* ptr = malloc(size + alignment - 1);
    if (ptr == NULL) {
        return ENOMEM; // 内存分配失败
    }

    uintptr_t addr = (uintptr_t)ptr;
    uintptr_t aligned_addr = (addr + alignment - 1) & ~(alignment - 1);

    // 为了保存原始指针地址,需要将指针地址存储在指针指针(memptr)指向的位置
    *(void**)memptr = (void*)aligned_addr;

    return 0; // 成功
}
  1. 为什么是void*的倍数

    void*的大小一般是机器字长,所以含义就是首先必须和机器字长对齐

  2. 如何检测出是2的幂次

    2的幂次数有个特性,那就是只有最高位是1,剩下的都是0,比如8,

    而减去1后,又变成最高位是0,剩下都是1

    8:   1000
    8-1: 0111
    

    此时求与,如果结果是0,那就是有2的幂次,否则就不是,

    这是只有2的幂次具有的性质

  3. 如何保证对齐到alignment

    (addr + alignment - 1) & ~(alignment - 1);
    

标签:字节,void,内存,对齐,alignment,addr
From: https://www.cnblogs.com/INnoVationv2/p/17579732.html

相关文章

  • java多线程内存图
    多线程的例子例一:publicclassTest{publicstaticvoidmain(String[]args)throwsException{Threadx1=newThread(){@Overridepublicvoidrun(){for(inti=0;i<100;i++){Syst......
  • GE反射内存卡的指标和型号
    产品特性:•1路发送,1路接收;•光纤高速网络2.12GHz;•最大256个节点;•光纤协议不占用CPU资源;•多模光纤节点距离300米;单模光纤节点距离10千米;•板载128M/256MByteSDRAM;•低延迟率(n秒级);•动态包长:每个包4到64或1M个字节。产品选型:1、CPCI接口-5565PIORC-110000(128MSDRAM多......
  • 关于B450M迫击炮主板插4根内存遇到的问题
    在京东光威买的两对套条,8Gx2,16Gx2,颗粒8G美光,16G南亚,总共4根DDR4在B450MMORTAR上反复尝试了无数次,开启XMPAuto3200得到的都是重启黑屏,蓝屏,内存检测错误,甚至把操作系统都搞坏了最后发现一个解决方案将BIOS更新到最新版本,我用的是1J1将16Gx2插2,4插槽,8Gx2插1,3插......
  • 常见的js内存泄漏
    1、 意外的全局变量。未被声明的变量,会被挂在window对象下,不能及时的销毁。2、计时器和回调函数timers。定时器setInterval或者setTimeout在不需要使用的时候,没有被clear,导致定时器的回调函数及其内部依赖的变量都不能被回收,这就会造成内存泄漏。3、DOM泄漏。(1)给DOM对象添加的属......
  • c++打印类的内存布局
    内存布局默认32位编译下,4字节对齐有虚函数情况下会在内存一开始多一个虚表指针普通函数不占内存空间静态成员不占内存空间1.通过cl命令输出hello.cpp中的类A的内存布局测试#输出指定类型的内存布局clhello.cpp/d1reportSingleClassLayoutA#输出所有类型的内存布局......
  • android sdk 内存mac
    如何实现AndroidSDK内存mac作为一名经验丰富的开发者,我很高兴帮助你了解如何实现AndroidSDK内存mac。在开始之前,我们先来了解一下整个过程的流程。流程下面是实现AndroidSDK内存mac的步骤:步骤描述1.安装AndroidStudio2.创建一个新的Android项目3......
  • android ndk内存泄露检测与定位
    AndroidNDK内存泄漏检测与定位简介在Android开发中,经常会遇到内存泄漏的问题。而使用NDK进行开发时,由于与底层交互更加频繁,内存泄漏问题也更易发生。本文将介绍如何在AndroidNDK中进行内存泄漏检测与定位的流程和具体实现方法。流程概述以下是进行AndroidNDK内存泄漏检测与......
  • Delphi7 TClientDataSet作为内存数据集合使用
    IDE:Delphi7使用TClientDataSet控件在Delphi中保存内存数据集合(相当于Java中的List<Map>),代码片段:procedureTMainForm.btnExportClick(Sender:TObject);tmpCds:TClientDataSet;tmpStr:string;begin//TClientDataSet作为内存数据集合使用//*********************......
  • PerfView 洞察C#托管堆内存 "黑洞现象"
    一:背景1.讲故事首先声明的是这个黑洞是我定义的术语,它是用来表示内存吞噬的一种现象,何为内存吞噬,我们来看一张图。从上面的卦象图来看,GCHeap的Allocated=852M和Committed=16.6G,它们的差值就是分配缓冲区=16G,缓冲区的好处就是用空间换时间,弊端就是会实实在在的侵......
  • C++内存分区模型
    C++内存分区模型在执行C++程序的过程中,内存大致分为四个区域:栈区(Stack):用于实现函数调用。由编译器自动分配释放,存放函数的参数值和局部变量等堆区(Heap):用于存放动态分配的变量。由程序员动态分配和释放,使用new和delete操作符全局/静态存储区(DataSegment&BSSSegm......