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 ...
随机推荐
- 教程10--hexo搭建
1.安装node.js 下载系统对应的node安装包一直下一步完成 2.安装git 参照git安装https://www.cnblogs.com/daxiang2008/p/10687616.html ...
- Java课程设计---WindowBuilder插件安装
1 .获取插件地址 WindowBuilder 地址http://www.eclipse.org/windowbuilder/download.php 打开网址后会看到如下 2.在线方式安装插件 根据 ...
- git for windows下载
https://npm.taobao.org/mirrors/git-for-windows/ 国内的镜像 转载自:https://blog.csdn.net/ee230/article/detail ...
- List分组和排序
1 //分组 2 var ll= lst.GroupBy(x => new { x.Id, x.Name }).Select(x => new 3 { 4 Key = x.Key, 5 I ...
- kNN(k近邻)算法代码实现
目标:预测未知数据(或测试数据)X的分类y 批量kNN算法 1.输入一个待预测的X(一维或多维)给训练数据集,计算出训练集X_train中的每一个样本与其的距离 2.找到前k个距离该数据最近的样本-- ...
- php 原生分页
<?php // 连接数据库 $link = mysqli_connect("127.0.0.1", "root", "root", ...
- JavaScript面向对象—深入ES6的class
JavaScript面向对象-深入ES6的class 前言 在前面一篇中主要介绍了JavaScript中使用构造函数+原型链实现继承,从实现的步骤来说还是比较繁琐的.在ES6中推出的class的关键字 ...
- hive从入门到放弃(二)——DDL数据定义
前一篇文章,介绍了什么是 hive,以及 hive 的架构.数据类型,没看的可以点击阅读:hive从入门到放弃(一)--初识hive 今天讲一下 hive 的 DDL 数据定义 创建数据库 CREAT ...
- MySQL报错 SQL ERROR:1064 ,SQLState:42000
使用mysql新增数据时报错,具体信息如图所示: 错误原因: 所建的表中 表名或字段名与数据库关键字冲突 解决办法 可以根据报错信息,查看错误的具体位置,找到数据库中对应的字段,查询是否与关键字(不分 ...
- Bert不完全手册3. Bert训练策略优化!RoBERTa & SpanBERT
之前看过一条评论说Bert提出了很好的双向语言模型的预训练以及下游迁移的框架,但是它提出的各种训练方式槽点较多,或多或少都有优化的空间.这一章就训练方案的改良,我们来聊聊RoBERTa和SpanBER ...