netcore高级知识点,内存对齐,原理与示例

news/2024/11/15 16:01:39/文章来源:https://www.cnblogs.com/hugogoos/p/18391514

最近几年一直从事物联网开发,与硬件打交道越来越多,发现越接近底层开发对性能的追求越高,毕竟硬件资源相对上层应用来实在是太缺乏了。今天想和大家一起分享关于C#中的内存对齐,希望通过理解和优化内存对齐,可以帮助大家更好的提高程序性能以及资源利用效率。

什么是内存对齐

内存对齐指把数据存储在内存中时,需要按照某种特定规则进行存储,使其内存存储在符合特定边界要求的内存地址上。而内存对齐主要目的则是减少CPU内存操作次数,提高内存操作效率,并提升CPU缓存命中率,从而提升整体性能。

内存对齐原则

内存对齐原则包含两部分:内存对齐边界和内存对齐规则。

    内存对齐边界:数据存储在内存中的起始内存地址必须满足条件。例如,8字节对齐则要求数据的起始内存地址必须是8的倍数;

    内存对齐规则:不同的硬件平台内存对齐规则也各有差异,比如:x86、x64架构在内存对齐方面比较宽松,而ARM、RISC-V架构则相对比较严格;一般32位处理器要求4字节对齐,而64位处理器要求8字节对齐;

因此不同的CPU架构和平台则内存对齐规则也各有不同,而这些差异也都是为了使数据在内存中的布局更加符合CPU操作方式,从而提高程序执行效率。 

C#中的内存对齐

1、“托管代码”和“非托管代码”

托管代码:执行过程交给运行时CLR管理的代码,运行时CLR负责提取托管代码并编译成机器代码最后执行,同时运行时CLR还负责自动内存管理、安全边界和类型安全等重要服务。

“非托管代码”:即不被运行时CLR管理的代码,比如运行C/C++语言编写的代码,而此时开发任意就需要亲自处理很多事情,比如内存管理、垃圾回收、安全问题等等。

因此一般对于托管代码来说,内存的分配以及对齐策略都被运行时CLR一手包办了,无需我们过多关注,而如果需要通过P/Invoke和COM互操作来调用非托管代码则需要开发者自己处理内存对齐策略了。

当然也不是说纯托管代码就没有对内存对齐操作空间了,只是相对来说与非托管代码交互时使用内存对齐操作空间更大。

2、StructLayoutAttribute特性

无论托管内存还是非托管内存,都可以用StructLayoutAttribute特性来对其进行内存布局控制,简单来说对于托管代码可以使用LayoutKind枚举值Explicit进行显示控制,而对于非托管代码LayoutKind枚举值都可以控制。

示例-字段顺序影响内存占用大小

我们用StructLayout(LayoutKind.Sequential标记OriginalLayout结构体,看看每个字段的布局情况及其与占用内存总大小之间的关系,先来看下面一段代码:

using System.Runtime.InteropServices;
namespace CSharp
{public class MemoryLayout{[StructLayout(LayoutKind.Sequential)]public struct OriginalLayout{public long LongField1;public short ShortField;public byte ByteField1;}public static void Run(){Console.WriteLine($"OriginalLayout LongField1 偏移量: {Marshal.OffsetOf(typeof(OriginalLayout), "LongField1")} ");Console.WriteLine($"OriginalLayout ShortField 偏移量: {Marshal.OffsetOf(typeof(OriginalLayout), "ShortField")} ");Console.WriteLine($"OriginalLayout ByteField1 偏移量: {Marshal.OffsetOf(typeof(OriginalLayout), "ByteField1")} ");Console.WriteLine($"OriginalLayout 总大小: {Marshal.SizeOf(typeof(OriginalLayout))} bytes");Console.ReadKey();}}
}

我们使用Marshal.OffsetOf计算每个字段偏移量,即第一个字段偏移量表示其内存地址为0,则第二个字段偏移量表示为其相对第一个字段内存地址值的相对值,使用Marshal.SizeOf计算类型所占内存总大小。

如下图是上面代码运行结果:

首先说下long类型为8字节、short类型为2字节、byte类型为1字节,再来详细说下每个值怎么来的。

首先因为LongField1是第一个字段所以为0,并且因为long类型为8字节,所以LongField1使用了0-7内存地址段,所有第二个字段ShortField偏移量为8,因此ShortField使用了8-9内存地址段,所以第三个字段ByteField1偏移量为10。

那为什么总大小不是8+2+1=11字节,而16字节呢?这是因为对于类型的对齐方式默认会以其最大的元素对齐方式为准,并且整个类型大小是最大元素大小的整数倍,因此这里的总大小是8的倍数,因为2+1并没有占满8字节,因此ByteField1后面被自动填充了5个字节,以此达到对齐要求。所以最后就是8+2+1+5(自动填充)=16字节。

然后我们把LongField1和ShortField两个字段调整一下位置,再来看看运行结果:

public class MemoryLayout
{[StructLayout(LayoutKind.Sequential)]public struct OriginalLayout{public short ShortField;public long LongField1;public byte ByteField1;}public static void Run(){Console.WriteLine($"OriginalLayout ShortField 偏移量: {Marshal.OffsetOf(typeof(OriginalLayout), "ShortField")} ");Console.WriteLine($"OriginalLayout LongField1 偏移量: {Marshal.OffsetOf(typeof(OriginalLayout), "LongField1")} ");Console.WriteLine($"OriginalLayout ByteField1 偏移量: {Marshal.OffsetOf(typeof(OriginalLayout), "ByteField1")} ");Console.WriteLine($"OriginalLayout 总大小: {Marshal.SizeOf(typeof(OriginalLayout))} bytes");Console.ReadKey();}
}

这里为什么又是24字节呢?

首先虽然ShortField只占了2字节,使用了0-1内存地址段,但是LongField1并不能从2内存地址值开始排版,因为每个字段必须与其自身大小的字段或类型的对齐方式对齐,也就是说LongField1占8字节,那么其内存地址起始值也要是8的整数倍,因此LongFiled1使用了8-15内存地址段,而ShortField和LongFiled1之间会被自动填充6个字节,同样的ByteField1后面也被自动填充7个字节,因此总大小为24字节。

这里只是举了个小例子来展示字段顺序不同,对最终类型所占内存总大小的,这也给我们设计低内存消耗程序设计提供了空间。

当然这里只是简单使用了StructLayout,还Pack属性,以及Explicit和FieldOffset,还有CharSet、MarshalAs等复杂的功能都没有介绍,有兴趣的可以深入研究研究。本文只是简单内存对齐的原理原则以及简单的内存优化,后面有机会再给大家深入介绍。

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

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

相关文章

读软件开发安全之道:概念、设计与实施15安全测试

安全测试1. 安全测试 1.1. 测试是开发可靠、安全代码中的关键一环 1.2. 测试安全漏洞的目的是主动检测 1.3. 模糊测试是一种强大的补充技术,可以帮助我们找到更深层次的问题 1.4. 针对当前漏洞创建的安全回归测试,目的是确保我们不会再犯相同的错误 1.5. 大多数测试都是由执行…

张量感知量化杂谈

感知量化训练 QAT 传统的训练后量化将模型从 FP32 量化到 INT8 精度时会产生较大的数值精度损失。感知量化训练(Aware Quantization Training)通过在训练期间模拟量化操作,可以最大限度地减少量化带来的精度损失。 QAT 的流程,如图7-9所示,首先基于预训练好的模型获取计算…

张量量化方法杂谈

量化方法对比 QAT 可以达到的精度较高,但是往往需要较多的量化训练时间,量化成本比较大。PTQ 的量化过程比较迅速,只需要少量数据集来校准,但是量化后精度往往损失较多,见表7-1。 表7-1 量化方法参数对比量化方法功能经典适用场景使用条件易用性精度损失预期收益量化训练 …

深度学习模型优化杂谈

深度学习模型优化概述 模型压缩跟轻量化网络模型不同,压缩主要是对轻量化或者非轻量化模型执行剪枝、蒸馏、量化等压缩算法和手段,使得模型更加小、更加轻便、更加利于执行。 基本介绍 随着神经网络模型的复杂性和规模不断增加,模型对存储空间和计算资源的需求越来越多,使得…

【新型进程注入】一种绕过EDR和XDR的新型进程注入技术

1.技术研究背景 2.进程注入 3.Windows API监控 4.认识下新的注入技术 5.查找有漏洞的DLL 6.注射方法 7.自注入和EDR脱钩 8.远程进程注入 9.安全影响 10.检测方法 11.结论1.技术研究背景 进程注入是指将恶意代码注入到某个目标进程内存空间, 使攻击者能够隐藏恶意代码并逃避检测…

【漏洞挖掘技巧】编码绕过

demo说明 初始请求: url/?f=etc/passwd在这里,我尝试通过URL参数f访问/etc/passwd文件,但服务器返回了403状态码,表示访问被禁止。这是因为Web应用防火墙(WAF)检测到请求试图访问敏感文件,因此拦截了该请求 Base64编码:为了绕过WAF的检测,我们对字符串/etc/passwd进行…

给 Minecraft 的 MTR 网页地图换个字体

介绍 呃。。。其实就是感觉 MTR 自带的那个地图有点不太合自己口味,就简单拆包改一下 CSS! 修改 找到 Jetty 目录 右键用压缩包打开 .jar,依次打开 assets/mtr/website 即网页。我这里就简单的改了下 index.css 和 utilities.js。还需要单独改一个 JS 文件是因为 MTR 的地图…

VR虚拟驾驶未来发展以及汽车自动驾驶的特点

在自动驾驶汽车的基础上,VR虚拟现实技术的应用也让自动驾驶汽车更加智能化,能够实现更高级的驾驶体验,今天这篇文章就和大家一起探讨一下 VR虚拟驾驶未来发展的趋势,以及虚拟现实自动驾驶汽车所带来的几个改变。在自动驾驶汽车的基础上,VR虚拟现实技术的应用也让自动驾驶汽…

对 Minecraft 的 Dynmap 做一些小美化

介绍 Dynmap 是 Minecraft 中以网页 Web 形式呈现地图的模组,和 BlueMap 等类似。我自己倒是 Dynmap 用多了感觉更习惯一些就一直用下去了,虽然如今 BlueMap 之类的确实更先进。 LiveAtlas LiveAtlas 是 Dynmap 的第三方皮肤扩展,下载好后直接导入 dynmap/web 文件夹覆盖即可…

docker 环境下的 iptables 复杂配置

最近在项目中,遇到了一个比较辣手的 iptables 规则配置问题。记录一下简化一下问题: 本文中使用到 docker-compose 服务启动示例如下,虚拟机 IP 为 192.168.111.138 services:db:image: mariadb:ltscontainer_name: mysql_zdprestart: alwaysports:- "3306:3306"en…

天下良仓订单导入功能开发解析——生鲜配送供应链系统_杭州生鲜配送系统之升鲜宝_全国首创,简单、方便、高校,无须api 接口对接,少看脸色。

天下良仓_学校食材教育系统_订单导入功能开发解析——生鲜配送供应链系统_杭州生鲜配送系统之升鲜宝_全国首创,简单、方便、高校,无须api 接口对接,少看脸色。对接“杭州天下粮仓系统”的API接口有其好处与坏处。以下是一些常见的优缺点分析: ### 好处1. **自动化和效率提升…

Go plan9 汇编:说透函数栈

原创文章,欢迎转载,转载请注明出处,谢谢。0. 前言 函数是 Go 的一级公民,本文从汇编角度出发看看我们常用的一些函数在干什么。 1. 函数 1.1 main 函数 在 main 函数中计算两数之和如下: package mainfunc main() {x, y := 1, 2z := x + yprint(z) }使用 dlv 调试函数(不了…