Rust 中的数据布局-repr
repr(Rust)
首先,所有类型都有一个以字节为单位的对齐方式,一个类型的对齐方式指定了哪些地址可以用来存储该值。一个具有对齐方式n的值只能存储在n的倍数的地址上。所以对齐方式 2 意味着你必须存储在一个偶数地址,而 1 意味着你可以存储在任何地方。对齐至少是 1,而且总是 2 的幂。
基础类型通常按照其大小对齐,尽管这是特定平台的行为。例如,在 x86 上u64和f64通常被对齐到 4 字节(32 位)。
一个类型的大小必须始终是其对齐方式的倍数(零是任何对齐方式的有效大小),这就保证了该类型的数组总是可以通过偏移其大小的倍数来进行索引。注意,在动态大小的类型的情况下,一个类型的大小和对齐方式可能不是静态的。
Rust 给你提供了以下方式来布置复合数据。
- structs (命名复合类型 named product types)
- tuples (匿名复合类型 anonymous product types)
- arrays (同质复合类型 homogeneous product types)
- enums (命名总和类型 —— 有标签的联合体 named sum types -- tagged unions)
- unions (无标签的联合体 untagged unions)
如果一个枚举的变体都没有相关联的数据,那么它就被称为无域。
默认情况下,复合结构的对齐方式等于其字段对齐方式的最大值。因此,Rust 会在必要时插入填充,以确保所有字段都正确对齐,并且整个类型的大小是其对齐的倍数。比如说:
struct A {
a: u8,
b: u32,
c: u16,
}
将在目标上以 32 位对齐,将这些基本类型对齐到它们各自的大小。因此,整个结构的大小将是 32 位的倍数。它可能变成:
struct A {
a: u8,
_pad1: [u8; 3], // 需要和 `b` 内存对齐
b: u32,
c: u16,
_pad2: [u8; 2], // 让总体的大小是 4 的倍数
}
或者,也许:
struct A {
b: u32,
c: u16,
a: u8,
_pad: u8,
}
所有数据都存储在结构中,正如你在 C 语言中所期望的那样。然而,除了数组(密集包装且无序)之外,数据的布局在默认情况下没有指定。给出以下两个结构的定义:
struct A {
a: i32,
b: u64,
}
struct B {
a: i32,
b: u64,
}
Rust 确实保证 A 的两个实例的数据布局完全相同。然而,Rust 目前并不保证 A 的实例与 B 的实例具有相同的字段排序或填充。
对于我们编写的 A 和 B 来说,这一点似乎是迂腐的,但是 Rust 的其他几个特性使得该语言有必要以复杂的方式来处理数据布局。
例如,考虑这个结构:
struct Foo<T, U> {
count: u16,
data1: T,
data2: U,
}
现在考虑一下Foo<u32, u16>和Foo<u16, u32>的单态。如果 Rust 按照指定的顺序排列字段,我们希望它能对结构中的值进行填充以满足其对齐要求。因此,如果 Rust 不对字段重新排序,我们希望它能产生以下结果:
struct Foo<u16, u32> {
count: u16,
data1: u16,
data2: u32,
}
struct Foo<u32, u16> {
count: u16,
_pad1: u16,
data1: u32,
data2: u16,
_pad2: u16,
}
后一种情况很显然浪费了空间,对空间的最佳利用要求不同的单体有不同的字段排序。
枚举使这种考虑变得更加复杂,直观地说,一个枚举如下:
enum Foo {
A(u32),
B(u64),
C(u8),
}
可能会被布局成:
struct FooRepr {
data: u64, // 根据 tag 的不同,这一项可以为 u64,u32,或者 u8
tag: u8, // 0 = A,1 = B, 2 = C
}
事实上,这大约正是它的布局方式(根据tag的大小和位置来调整)。
然而,在一些情况下,这样的表述是低效的。这方面的典型案例是 Rust 的“空指针优化”:一个由单个外部单元变量(例如None)和一个(可能嵌套的)非空指针变量(例如Some(&T))组成的枚举,使得标签没有必要。空指针可以安全地被解释为单位(None)的变体。这导致的结果是,例如,size_of::<Option<&T>>() == size_of::<&T>()。
在 Rust 中,有许多类型会包含不可为空的指针,如Box<T>、Vec<T>、String、&T和&mut T。同样地,我们可以想象嵌套的枚举将它们的标记集中到一个单一的字段中,因为根据定义,它们的有效值范围有限。原则上,枚举可以使用相当复杂的算法,在整个嵌套类型中用禁止使用的值来存储枚举类型。因此,我们今天不指定枚举布局是特别符合预期的。
Rust 中的数据布局-repr的更多相关文章
- Rust 中的数据布局--可选的数据布局
Rust 允许你指定不同于默认的数据布局策略,并为你提供了不安全代码指南. repr(C) 这是最重要的"repr".它的意图相当简单:做 C 所做的事.字段的顺序.大小和对齐方式 ...
- Rust 中的数据布局--非正常大小的类型
非正常大小的类型 大多数的时候,我们期望类型在编译时能够有一个静态已知的非零大小,但这并不总是 Rust 的常态. Dynamically Sized Types (DSTs) Rust 支持动态大小 ...
- 借助 SIMD 数据布局模板和数据预处理提高 SIMD 在动画中的使用效率
原文链接 简介 为发挥 SIMD1 的最大作用,除了对其进行矢量化处理2外,我们还需作出其他努力.可以尝试为循环添加 #pragma omp simd3,查看编译器是否成功进行矢量化,如果性能有所提升 ...
- 【译】对Rust中的std::io::Error的研究
原文标题:Study of std::io::Error 原文链接:https://matklad.github.io/2020/10/15/study-of-std-io-error.html 公众 ...
- 如何在ASP.NET Web站点中统一页面布局[Creating a Consistent Layout in ASP.NET Web Pages(Razor) Sites]
如何在ASP.NET Web站点中统一页面布局[Creating a Consistent Layout in ASP.NET Web Pages(Razor) Sites] 一.布局页面介绍[Abo ...
- 在 ASP.NET 中创建数据访问和业务逻辑层(转)
.NET Framework 4 当在 ASP.NET 中处理数据时,可从使用通用软件模式中受益.其中一种模式是将数据访问代码与控制数据访问或提供其他业务规则的业务逻辑代码分开.在此模式中,这两个层均 ...
- WPF中的数据验证
数据验证 WPF的Binding使得数据能够在数据源和目标之间流通,在数据流通的中间,便能够对数据做一些处理. 数据转换和数据验证便是在数据从源到目标 or 从目标到源 的时候对数据的验证和转换. V ...
- c++继承中的内存布局
今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的.看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用. ——谈VC++对象模型(美)简.格雷程化 译 译者前言 一个C ...
- Rust 中的继承与代码复用
在学习Rust过程中突然想到怎么实现继承,特别是用于代码复用的继承,于是在网上查了查,发现不是那么简单的. C++的继承 首先看看c++中是如何做的. 例如要做一个场景结点的Node类和一个Sprit ...
随机推荐
- 知识增广的预训练语言模型K-BERT:将知识图谱作为训练语料
原创作者 | 杨健 论文标题: K-BERT: Enabling Language Representation with Knowledge Graph 收录会议: AAAI 论文链接: https ...
- 由浅入深--MyBatis系列
从今天开始将要开始由浅入深--MyBatis系列博客的编写; 主要目录如下: MyBatis基础知识篇 1.由浅入深---ORM简介 2.由浅入深---MyBatis的整体架构 3.由浅入深---第一 ...
- Linux中查看进程与日志
转至:https://www.cnblogs.com/dengxiaoning/p/13336778.html Linux尽管使用频繁,仍然每次都还是需要到处去找相关的命令,如进程,日志之类的,既然这 ...
- 如何通过xstart远程连接桌面
转至:https://www.cnblogs.com/LiuChang-blog/p/12324193.html 1.1.安装依赖包: (1)安装语言包: [root@slave-node2 ~]# ...
- VSCode好用的插件
一.Chinese: 汉化 二.Live Server: Live Serve这是一个为静态和动态页面启动具有实时重新加载功能的开发本地服务器. 安装好后,在html页面右键:Open With Li ...
- jmeter 24个常用函数
Jmeter_24个常用函数 JMeter提供了很多函数,如果能够熟练使用,可以为脚本带来很多方便. JMeter函数是一种特殊值,可用于除测试计划外的任何组件. 函数调用的格式如下所示:${__ ...
- 如何在矩池云上运行FinRL-Libray股票交易策略框架
FinRL-Libray 项目:https://github.com/AI4Finance-LLC/FinRL-Library 选择FinRL镜像 在矩池云-主机市场选择合适的机器,并选择FinRL- ...
- tp 5 框架 ajax软删除,回收站,数据恢复
//HTML代码: <td> <span onclick="del({$v.id})">删除</span> </td> //ajax ...
- springboot Redistemplate的execute和 executePipelined
springboot 的 RedisTemplate 的 execute 和 executePipelined 功能的区别 1.execute 以下是 springboot 官网原文: Redis p ...
- Kubernetes:服务与负载均衡
Blog:博客园 个人 参考:Service | Kubernetes.<Kubernetes进阶实战> 有了 Workload,我们可以方便地管理多实例的应用,但是要想能够方便地访问应用 ...