数据对齐(结构体对齐、类成员对齐、动态内存对齐/指针对齐、函数参数对齐、SIMD对齐)

数据对齐

数据对齐是一种计算机内存管理技术,确保数据存储在内存中的特定地址上,以提高访问效率和性能。
不同的数据类型(如整数、浮点数、指针等)在内存中的存储位置通常需要满足特定的边界要求,即数据的起始地址是其大小的整数倍。
通过对数据进行对齐,可以减少处理器在访问不对齐的数据时可能产生的额外开销。
对齐可以在编译器级别通过设置编译选项或使用特定的关键字来控制,也可以使用一些预处理指令来进行调整。

总之,数据对齐是为了提高内存访问效率和性能,将数据存储在适当的内存地址上,以减少额外的开销和性能损失。

数据对齐主要体现于:结构体对齐、类成员对齐、动态内存对齐、函数参数对齐、SIMD对齐等形式。

结构体对齐

  • 最大成员对齐:结构体的对齐通常取决于结构体中最大的成员的大小。编译器会将结构体成员对齐到最大成员大小的整数倍。
  • 填充字节:为了满足对齐要求,编译器可能会在结构体成员之间插入一些填充字节,使得下一个成员能够按照适当的对齐方式存储。
  • 指定对齐方式:某些编译器允许开发人员通过预处理指令或关键字来指定结构体的对齐方式,以满足特定的需求。
  • 位域对齐:给基本数据类型使用位域指定其占用位数。
#include <iostream>// 默认对齐方式
// 若结构体没有大于4字节的类型,一般为4字节对齐,本文以一个对齐字节默认为4个字节为例
struct Struct1
{char a[20]; // 160 bits,占用5个对齐字节int b;      // 32 bits,占用1个对齐字节int c : 33; // 33 bits,余1位,余的那一位占用下一个对齐字节的最低位。占用2个对齐字节int d;      // 32 bits,占用1个对齐字节
};
// 本结构体的大小为4*(5+1+2+1)=36个字节
// 说明:c使用位域为33导致余的那一位所在的对齐字节剩下的31位不足以装下d的32个位,
//  于是c的32+1个位一共占用2个对齐字节,而d额外占用一个对齐字节// 指定最大成员对齐方式为 4 字节
#pragma pack(push, 4)
struct Struct2
{char a[19]; // 152 bits,占用5个对齐字节int b;      // 32 bits,占用1个对齐字节double c;   // 64 bits,占用2个对齐字节char d[4];  // 32 bits,占用1个对齐字节
};
#pragma pack(pop)
// 本结构体的大小为4*(5+1+2+1)=36个字节
// 说明:a占用5个对齐字节却占不满,还有8bits的空间,但8bits不足以装下b,于是这8bits空间由空位域填满,
//  而c单独占用2个对齐字节。d所在对齐字节还有4字节空间由空位域填满。// 指定最大成员对齐方式为 8 字节
#pragma pack(push, 8)
struct Struct3
{char a[16]; // 128 bits,占用2个对齐字节int b;      // 32 bits,占用1个对齐字节double c;   // 64 bits,占用1个对齐字节char d[4];  // 32 bits,占用1个对齐字节
};
#pragma pack(pop)
// 本结构体的大小为8*(2+1+1+1)=40个字节
// 说明:b所在对齐字节剩下的4字节不够c用,于是c额外占用1个对齐字节,而b所在对齐字节由空位域填满余下4个字节
//  d所在的对齐字节未满,空位域填充余下4个字节int main()
{std::cout << "Size of Struct1: " << sizeof(Struct1) << " bytes" << std::endl;std::cout << "Size of Struct2: " << sizeof(Struct2) << " bytes" << std::endl;std::cout << "Size of Struct3: " << sizeof(Struct3) << " bytes" << std::endl;return 0;
}/*
./"test"
Size of Struct1: 36 bytes
Size of Struct2: 36 bytes
Size of Struct3: 40 bytes
*/

类成员对齐

  • 数据成员的对齐:每个数据成员都有一个对齐要求,这是指数据成员在内存中的起始地址必须是对齐要求的整数倍。例如,int 类型通常需要4字节对齐,double 类型通常需要8字节对齐。
  • 继承的对齐:派生类的成员对齐规则受基类和派生类成员的影响。在多继承情况下,编译器会根据继承的顺序和基类成员的对齐规则来确定派生类的成员排列。
  • 虚函数表指针(vptr)的对齐:对于包含虚函数的类,编译器通常会在类中添加一个指向虚函数表的指针(vptr)。vptr 的对齐方式可能会影响整个类的对齐方式。
#include <iostream>// 默认对齐方式为 4 字节
class MyClass1
{
public:char a[20]; // 160 bits,占用 5 个对齐字节int b;      // 32 bits,占用 1 个对齐字节int c : 33; // 33 bits,占用 2 个对齐字节int d;      // 32 bits,占用 1 个对齐字节
};// 指定最大成员对齐方式为 4 字节
#pragma pack(push, 4)
class MyClass2
{
public:char a[19]; // 152 bits,占用 5 个对齐字节int b;      // 32 bits,占用 1 个对齐字节double c;   // 64 bits,占用 2 个对齐字节char d[4];  // 32 bits,占用 1 个对齐字节
};
#pragma pack(pop)// 指定最大成员对齐方式为 8 字节
#pragma pack(push, 8)
class MyClass3
{
public:char a[16]; // 128 bits,占用 2 个对齐字节int b;      // 32 bits,占用 1 个对齐字节double c;   // 64 bits,占用 1 个对齐字节char d[4];  // 32 bits,占用 1 个对齐字节
};
#pragma pack(pop)int main()
{std::cout << "Size of MyClass1: " << sizeof(MyClass1) << " bytes" << std::endl;std::cout << "Size of MyClass2: " << sizeof(MyClass2) << " bytes" << std::endl;std::cout << "Size of MyClass3: " << sizeof(MyClass3) << " bytes" << std::endl;return 0;
}

动态内存对齐 / 指针对齐

动态内存的分配和对齐在 C++ 中也是一个重要的概念。通常情况下,动态内存分配使用 new 和 delete 运算符(或者使用 malloc 和 free 函数),而这些函数会分配内存并返回指向该内存块的指针。

在动态内存分配过程中,对齐的概念同样也适用。通常情况下,动态内存分配的指针所指向的内存块会满足平台默认的对齐要求。如果需要特定对齐要求,可以使用 C++11 引入的 std::aligned_alloc 函数,它允许您指定所需的对齐方式。

以下是一个示例,演示了如何使用 std::aligned_alloc / std::posix_memalign 进行动态内存分配,并指定特定的对齐方式:

在这里插入代码片

函数参数对齐

在函数参数传递过程中,参数的对齐方式也是一个重要的概念。函数参数的对齐方式与内存对齐类似,会影响内存的使用情况和性能。

通常情况下,函数参数在调用栈上分配内存,而调用栈的分配和对齐方式可能会受到编译器、操作系统和平台的影响。不同的编译器和平台可能有不同的规则,但是一般来说,函数参数的对齐方式会遵循与数据类型有关的规则。

在实际编程中,大多数情况下,编译器会自动处理函数参数的对齐方式,确保程序的正确性和性能。

SIMD对齐

SIMD(Single Instruction, Multiple Data)是一种并行计算技术,旨在通过在单个指令中同时处理多个数据元素来提高计算性能。在使用SIMD指令集进行向量化计算时,数据需要按照特定的对齐要求存储在内存中,以便CPU能够高效地执行向量化操作。

SIMD对齐通常是按照硬件架构的要求进行的,以确保数据可以被SIMD指令正确地加载和处理。具体的对齐要求取决于使用的SIMD指令集和CPU架构。通常,SIMD对齐要求为16字节、32字节或更大。

为了实现SIMD对齐,您需要确保向量数据按照正确的字节边界进行存储。这可能涉及到在内存分配、数据传输和数据结构定义中使用特定的对齐指令或属性。

在C++中,您可以使用一些编译器指令或属性来控制数据的对齐,以便适应SIMD要求。例如,对于GCC编译器,可以使用__attribute__((aligned(n)))来指定数据的对齐方式,其中n表示字节对齐数。

以下是一个示例,展示如何在C++中使用GCC的对齐属性来实现SIMD对齐:

#include <iostream>// 定义一个结构体,其中包含一个需要SIMD对齐的数据数组
struct AlignedData
{float data[4] __attribute__((aligned(16))); // 使用 GCC 对齐属性,要将数组按照16字节边界进行对齐
};int main()
{AlignedData alignedArray;// 输出地址,以验证对齐是否生效std::cout << "Address of alignedArray: " << &alignedArray << std::endl;return 0;
}/** ./"test"* Address of alignedArray: 0x7fffd5c3ca10*//** 说明:对于16字节边界对齐,地址0x7fffd5c3ca10是否满足取决于该地址的末尾4位是否为零。* 如果末尾4位为零,那么该地址就是16字节对齐的,否则就不是。* 在16进制表示中,每个十六进制数字对应4个二进制位。* 因此,16字节的倍数的地址末尾4位是否都是零,可以通过以下方式来验证:* 将地址的末尾4位转换为二进制,检查这4位二进制是否都是零。* 例如,对于地址0x7fffd5c3ca10:* 0x10 转换为二进制为 0001 0000。* 这4位二进制都是零,因此地址0x7fffd5c3ca10是16字节对齐的。* 这种验证方法适用于所有的地址,只要将地址转换为二进制并检查末尾4位即可判断是否满足16字节对齐。**/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/46722.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

公众号11周年,终于向公域流量打开了大门

是的&#xff0c;在这篇文章要发布之前&#xff0c;看了下日期&#xff0c;才惊觉明天就是公众号11周年了。 时间真的过得飞快&#xff0c;总觉得10周年刚过不久。 已经11年的公众号&#xff0c;或许少了很多关注&#xff0c;或许很多目光也被视频号夺去了。 但让人欣喜的是…

前馈神经网络dropout实例

直接看代码。 &#xff08;一&#xff09;手动实现 import torch import torch.nn as nn import numpy as np import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt#下载MNIST手写数据集 mnist_train torchvision.datasets.MN…

基于开源模型搭建实时人脸识别系统(四):人脸质量

续人脸识别实战之基于开源模型搭建实时人脸识别系统&#xff08;三&#xff09;&#xff1a;人脸关键点、对齐模型概览与模型选型_CodingInCV的博客-CSDN博客 不论对于静态的人脸识别还是动态的人脸识别&#xff0c;我们都会面临一个问题&#xff0c;就是输入的人脸图像的质量可…

使用 OpenTelemetry 构建可观测性 04 - 收集器

在之前的博文中&#xff0c;我们讨论了如何使用 SDK 和链路追踪生产者来导出进程中的遥测数据。尽管有多种类型的导出器可供选择&#xff0c;但其中一个常见的目标是将数据导出到 OpenTelemetry Collector。本篇文章将深入探讨收集器以及如何使用它。 选 OTel Collector 还是…

Spring中的依赖注入(Dependency Injection)有哪些不同的方式?可以举例说明吗?

Spring中的依赖注入&#xff08;Dependency Injection&#xff09;有哪些不同的方式&#xff1f;可以举例说明吗&#xff1f; 构造方法注入 使用构造函数进行DI注入 public class MyClass {private MyDependency myDependency;public MyClass(MyDependency myDependency) {t…

Eduma主题 - 线上教育WordPress主题/网站

Eduma主题 – 线上教育WordPress主题是为教育网站、LMS、培训中心、课程中心、学院、大学、学校、幼儿园而制作的。基于我们使用以前的主题eLearning WP构建WordPress LMS的经验&#xff0c;Education WP是下一代&#xff0c;也是围绕WordPress最好的教育主题之一&#xff0c;它…

C++编码规范解读

规范 C文件名和类名保持一致 好处&#xff1a;代码整体结构清晰、明了。java里强制如此。 类型命名采用大驼峰 比如&#xff1a;UrlEncoder FileParser 优先使用 头文件中的基本类型 如&#xff1a; 有符号类型 无符号类型 描述 int8_t uint8_t 宽度恰为8的有/无符号整…

C++并发多线程--std::async创建异步任务是否创建线程

1--std::async创建异步任务 std::async 创建一个异步任务&#xff0c;其不一定会创建一个新线程去执行该任务&#xff1b; 使用 std::launch::deferred 时&#xff0c;异步任务不会创建一个新线程&#xff1b; 使用 std::launch::async 时&#xff0c;操作系统会强制创建一个新…

一个炫酷的头像悬停效果 2

基于上次翻译的 &#x1f525;&#x1f525;一个炫酷的头像悬停效果 收获了不少同学的喜欢&#xff0c;原作者近期进行了优化升级。本文将升级后的核心实现过程进行梳理讲解&#xff0c;如果没看过第一期的推荐先看看第一期的实现过程。升级后的效果如下图所示。 gif动画效果如…

Baklib是比语雀、Notion、石墨文档更好用的在线知识库管理工具

在当今信息爆炸的时代&#xff0c;如何高效地管理和利用知识成为了每个人都面临的问题。在线知识库管理工具应运而生&#xff0c;帮助用户整理、存储和共享知识。在这篇文章中&#xff0c;我将介绍一个更好用的在线知识库管理工具——Baklib&#xff0c;并探讨它相对于其他知识…

变上限积分求导

y ∫ 0 x t f ( t 2 − x 2 ) d t y \int _0^x t f( t^2 - x^2)dt y∫0x​tf(t2−x2)dt 设 t 2 − x 2 u , 那么 t u x 2 , d t d u 2 u x 2 &#xff0c; 并且当 t x 时 u 0 , 当 t 0 时&#xff0c; u − x 2 设 t^2 - x^2 u,那么t \sqrt{ux^2},dt \frac{du}{2\s…

Spring系列篇--关于IOC【控制反转】的详解

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Spring的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是Spring 二.Spring的特点 三.什…

【O2O领域】Axure外卖订餐骑手端APP原型图,外卖众包配送原型设计图

作品概况 页面数量&#xff1a;共 110 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;外卖配送、生鲜配送 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本品为外卖订餐骑手端APP原型设计图&#x…

Datawhale Django 后端开发入门 Task05 DefaultRouter、自定义函数

一、DefaultRouter是Django REST framework中提供的一个路由器类&#xff0c;用于自动生成URL路由。路由器是将URL与视图函数或视图集关联起来的一种机制。Django REST framework的路由器通过简单的配置可以自动生成标准的URL路由&#xff0c;从而减少了手动编写URL路由的工作量…

Redis Lua脚本执行原理和语法示例

Redis Lua脚本语法示例 文章目录 Redis Lua脚本语法示例0. 前言参考资料 1. Redis 执行Lua脚本原理1.1. 对Redis源码中嵌入Lua解释器的简要解析&#xff1a;1.2. Redis Lua 脚本缓存机制 2. Redis Lua脚本示例1.1. 场景示例1. 请求限流2. 原子性地从一个list移动元素到另一个li…

基于郊狼算法优化的BP神经网络(预测应用) - 附代码

基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.郊狼优化BP神经网络2.1 BP神经网络参数设置2.2 郊狼算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

【深入解析:数据结构栈的魅力与应用】

本章重点 栈的概念及结构 栈的实现方式 数组实现栈接口 栈面试题目 概念选择题 一、栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数…

指针(一)【C语言进阶版】

大家好&#xff0c;我是深鱼~ 【前言】&#xff1a; 指针的主题&#xff0c;在初阶指针章节已经接触过了&#xff0c;我们知道了指针的概念&#xff1a; 1.指针就是个变量&#xff0c;用来存放地址&#xff0c;地址的唯一标识一块内存空间&#xff08;指针变量&#xff09;&a…

【云原生|Docker系列第3篇】Docker镜像的入门实践

欢迎来到Docker入门系列的第三篇博客&#xff01;在前两篇博客中&#xff0c;我们已经了解了什么是Docker以及如何安装和配置它。本篇博客将重点介绍Docker镜像的概念&#xff0c;以及它们之间的关系。我们还将学习如何拉取、创建、管理和分享Docker镜像&#xff0c;这是使用Do…

jenkins同一jar包部署到多台服务器

文章目录 安装插件配置ssh服务构建完成后执行 没有部署过可以跟这个下面的步骤先部署一遍&#xff0c;我这篇主要讲jenkins同一jar包部署到多台服务器 【Jenkins】部署Springboot项目https://blog.csdn.net/qq_39017153/article/details/131901613 安装插件 Publish Over SSH 这…