2411rust,正与整128
原文
长期以来,Rust在x86-32和x86-64架构上128位整数的对齐与C语言不一致.最近已解决此问题,但该修复带来了一些值得注意的效果.
作为用户,除非如下,否则不用担心:
1,假设i128/u128对齐,而不是用align_of
2,忽略improper_ctypes*检查,并在FFI中使用这些类.
除x86-32和x86-64外,其他架构不变.如果你的代码大量使用128位整数,会注意到运行时性能提高,但可能会增加内存使用.
背景
数据类型有两个与内存中的排列方式有关的内部值:大小和对齐.类型的大小是它在内存中消费的空间量,对齐指定了允许在哪些地址放置它.
像原语此类简单类型的大小一般是无歧义的,是它们所表示的数据的没有填充(未使用的空间)的确切大小.如,i64的大小总是为64位或8字节.
但是,对齐可能会有所不同.可在(1字节对齐)任意内存地址中保存8字节整数,但大多数64位计算机如果按8的倍数(8字节对齐)保存,则会取得最佳性能.
因此,与其他语言一样,Rust中的原语默认有该最有效的对齐.在创建复合类型时可见该效果:
use core::mem::{align_of, offset_of};
#[repr(C)]
struct Foo {a: u8, //1字节对齐b: u16, //2字节对齐
}
#[repr(C)]
struct Bar {a: u8, //1字节对齐b: u64, //8字节对齐
}
println!("Offset of b (u16) in Foo: {}", offset_of!(Foo, b));
println!("Alignment of Foo: {}", align_of::<Foo>());
println!("Offset of b (u64) in Bar: {}", offset_of!(Bar, b));
println!("Alignment of Bar: {}", align_of::<Bar>());
输出:
`Foo`中`b(u16)`的偏移:2`Foo`对齐:2`栏`中`b(u64)`的偏移:8`bar`对齐:8
看到,在一个结构中,总是在它的偏移是其对齐的倍数位置放置一个类型,即使表明未使用的空间,当不使用repr(C)时,Rust默认最小化它.
这些数字不是任意的;应用二进制接口(ABI)说明了它们应该是什么.在系统V(Unix&Linux)的x86-64psABI(处理器相关的ABI)中,图3.1:标量类型准确地告诉了应该如何表示原语:
C型 | Rust等价 | sizeof | 对齐(字节) |
|---|---|---|---|
符 | i8 | 1 | 1 |
正符 | u8 | 1 | 1 |
| 短 | i16 | 2 | 2 |
| 正短 | u16 | 2 | 2 |
| 长 | i64 | 8 | 8 |
正长 | u64 | 8 | 8 |
ABI仅指定了C类型,但Rust在兼容和性能优势方面都遵守相同定义.
错误的对齐问题
如果两个实现在数据类型的对齐上有分歧,则无法可靠地共享包含该类型的数据.Rust对128位类型的对齐不一致:
println!("alignment of i128: {}", align_of::<i128>());
//`rustc1.76.0`版本
// `i128`对齐:8
printf("alignment of __int128: %zu\n", _Alignof(__int128));
//`GCC`版本`13.2`
// __int128对齐:16
// Clang17.0.1
// __int128对齐:16
回头看一下psABI,可见Rust在此的对齐是错误的:
C型 | Rust等价 | sizeof | 对齐(字节) |
|---|---|---|---|
__int128 | i128 | 16 | 16 |
正__int128 | u128 | 16 | 16 |
表明,这并不是因为Rust积极地做错了什么:原语的布局来自Rust和Clang等语言使用的LLVMcodegen后端,且它有硬编码为8字节的i128对齐.
Clang使用正确的对齐只是因为变通,即在把类型交给LLVM前,手动按16字节设置对齐.这解决了布局问题,但也是其他一些小问题的根源.
Rust无此手动调整,因此在https://github.com/rustlang/rust/issues/54341上报告了它.
调用约定问题
还有一个问题:LLVM在按函数参数传递128位整数时,并不总是正确.在发现它与Rust相关前,这是LLVM中的一个已知问题.
调用函数时,会在寄存器中传递参数,直到没有更多的槽,然后会"溢出"到栈中(程序的内存).
ABI在3.2.3传递参数一节中,也告诉了该怎么做:
__int128类型的参数与INTEGER操作相同,但它们不适合一个通用寄存器,而是需要两个寄存器.为了分类,按如下实现对待__int128:
typedef struct {long low, high;
} __int128;
但在内存中保存的__int128类型的参数必须在16字节边界上对齐.
可手动实现调用约定来试此操作.在下面C示例中,用内联汇编按val为0x11223344556677889900aabbccddeeff值,来调用foo(0xaf,val,val,val).
x86-64使用RDI,RSI,RDX,RCX,R8和R9寄存器,来按顺序传递函数参数.每个寄存器适合一个字(64位),不合适的都压进栈中.
/*`<https://godbolt.org/z/5c8cb5cxs>`的完整示例*/
/*要查看问题,需要一个`内边距`值来"搞砸"参数对齐*/
void foo(char pad, __int128 a, __int128 b, __int128 c) {printf("%#x\n", pad & 0xff);print_i128(a);print_i128(b);print_i128(c);
}
int main() {asm(/*`加载`适合`寄存器`的参数*/"movl $0xaf,%edi\n\t"/*第1个槽位`(EDI)`:填充符(`"EDI"`是*与`"RDI"`相同,只是访问大小较小)*/"movq $0x9900aabbccddeeff,%rsi\n\t"/*第2个槽`(RSI):"a"`的下半部分*/"movq $0x1122334455667788,%rdx\n\t"/*第3个槽`(RDX):"a"`的上半部分*/"movq $0x9900aabbccddeeff,%rcx\n\t"/*第4个槽`(RCX):"b"`的下半部分*/"movq $0x1122334455667788,%r8\n\t"/*第5个槽位`(r8):'b'`的上半部分*/"movq $0xdeadbeef4c0ffee0,%r9\n\t"/*第6个槽`(R9)`:应该未使用,但来欺骗`Clang`!*//*重用保存的`寄存器`来加载栈*/"pushq %rdx\n\t"/*在栈上传递`'c'`的上半部分*/"pushq %rsi\n\t"/*在栈上传递`'c'`的下半部分*/"call foo\n\t"/*调用函数*/"addq $16,%rsp\n\t"/*重置栈*/);
}
使用GCC运行上述操作打印以下期望输出:
0xaf0x11223344556677889900aabbccddeeff0x11223344556677889900aabbccddeeff0x11223344556677889900aabbccddeeff
但是使用Clang17打印:
0xaf0x11223344556677889900aabbccddeeff0x11223344556677889900aabbccddeeff0x9900aabbccddeeffdeadbeef4c0ffee0^^^^^^^^^^^^^^^^这应该是下半部分^^^^^^^^^^^^^^^^很熟悉
惊喜!
这说明了第二个问题:LLVM期望i128在可能时一半在寄存器中传递,一半在栈上传递,但ABI禁止这样做.
因为该行为来自LLVM且没有合理的解决方法,因此这在Clang和Rust中都是一个问题.
方法
NikitaPopov修复了D158169的调用约定问题.这两项更改都已纳入LLVM18,即所有相关的ABI问题都使用在此版本的Clang和Rust中得到解决.
因为这些更改,Rust现在生成正确的对齐:
println!("alignment of i128: {}", align_of::<i128>());
//`rustc1.77.0`版本
i128对齐:16
如上,ABI指定数据类型对齐的部分原因是因为它在该架构上效率更高.更改手动对齐的初始性能运行,表明大大改进了编译器性能(严重依赖128位整数来处理整数文字).
增加对齐的缺点是在内存中复合类型并不总是很好地组合在一起,从而导致使用量增加.可惜,即需要牺牲一些性能优势,以避免增加内存成本.
兼容
总之,使用LLVM18(默认版本从1.78开始)的Rust的i128和u128将与版本的GCC及Clang18及更高版本(2024年3月发布)完全兼容.
相关文章:
2411rust,正与整128
原文 长期以来,Rust在x86-32和x86-64架构上128位整数的对齐与C语言不一致.最近已解决此问题,但该修复带来了一些值得注意的效果. 作为用户,除非如下,否则不用担心: 1,假设i128/u128对齐,而不是用align_of 2,忽略improper_ctypes*检查,并在FFI中使用这些类. 除x86-32和x86-64…...
将 HTML 转换为 JSX:JSX 和 JSX 规则
JSX 是 JavaScript 的语法扩展。您可以在 JavaScript 文件中编写 HTML 格式。 它基于 Web、Html、Css 和 JavaScript。Web 开发人员将页面内容分别编写为 Html 文件,将设计编写为 Css 文件,将逻辑编写为 JavaScript 文件。 须知 : JSX 是一个…...
将 FastAPI 部署到生产服务器(一套 全)
将 FastAPI 部署到生产服务器(全) 文章目录 将 FastAPI 部署到生产服务器(全)一、前言二、Fastapi项目 生产环境配置1. 准备环境2. 编写 FastAPI 应用3. 使用 Uvicorn 运行应用4. 配置生产级服务器 Gunicorn4.1 配置 Gunicorn 和 …...
题解 洛谷 Luogu P1873 [COCI 2011/2012 #5] EKO / 砍树 二分答案 C/C++
题目传送门: P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷 | 计算机科学教育新生态https://www.luogu.com.cn/problem/P1873思路: 很简单的二分答案 每次找区间中点 m,判断以 m 为高度砍下的木头是否够 h 即可 代码: #defin…...
SpringCloud SaToken整合微服务 集成Redis 网关路由权限拦截 服务间内部调用鉴权
介绍 作为 API 网关,通常负责路由、负载均衡、安全控制等功能。进行 统一鉴权 的做法意味着将所有微服务的认证和授权逻辑集中到网关层,而不是每个微服务单独实现。这样做有许多好处,微服务只关心核心业务逻辑,不需要处理身份验证…...
Oracle ADB 导入 BANK_GRAPH 的学习数据
Oracle ADB 导入 BANK_GRAPH 的学习数据 1. 下载数据2. 导入数据运行 setconstraints.sql 1. 下载数据 访问 https://github.com/oracle-quickstart/oci-arch-graph/tree/main/terraform/scripts,下载, bank_accounts.csvbank_txns.csvsetconstraints.…...
优化 MFC CGridCtrl 的表格布局与功能
在使用 MFC 的 CGridCtrl 控件创建表格时,遇到的一个典型问题是,当表格滚动条出现时,最后一列会显示空白。这篇博客将记录解决这一问题的详细过程,同时总结了 CGridCtrl 初始化及优化的关键步骤,帮助开发者快速搭建一个…...
koa-body 的详细使用文档
目录 koa-body install Features Hello World - Quickstart Usage with koa-router Usage with unsupported text body type Options 关于 parsedMethods 的说明 文件支持 关于未解析请求主体的说明 一些强大的选择 使用总结 koa-body 功能齐全的 koa body 解析器中…...
信息系统与互联网中的安全、隐私及伦理问题
1 伦理(Ethics) 1.1 伦理框架(Ethical Frameworks) 自然法与权利(Natural Law and Rights) 定义:基于人类自然权利的伦理思想,强调生命、自由和财产等基本权利。应用:隐…...
Java安全—log4j日志FastJson序列化JNDI注入
前言 log4j和fastjson都是这几年比较火的组件,前者是用于日志输出后者则是用于数据转换,今天我们从源码来说一下这两个组件为何会造成漏洞。 实验环境 这里的idea要进行一下配置,因为我们要引用第三方组件,而这些第三方组件都是…...
【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【DSP指令加速篇】
【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【DSP指令加速篇】 一、前文回顾二、CMSIS-NN简介2.1 为什么介绍CMSIS-NN?2.2 CMSIS-NN是什么?2.3 CMSIS-NN核心特性2.4 CMSIS-NN算子支持 三、TFLMCMSIS-NN集成3.1 包含TFLM的STM32项目3.2 理解TFLM…...
Python中如何判断一串文本是不是数字
目录 1. 内置函数2. 尝试类型转换3. 正则表达式 在编程中,我们经常需要确定一段文本是否为数字。 这不仅关系到数据的准确性,还涉及到后续的计算和处理。 1. 内置函数 在Python中,可以使用str.isdigit()、str.isnumeric()和str.isdecimal()…...
基于YOLOv8深度学习的智慧农业山羊行为检测系统研究与实现(PyQt5界面+数据集+训练代码)
随着智慧农业的快速发展,利用先进的技术手段对牲畜的行为进行自动化监测和管理,已经成为现代农业中的重要研究方向之一。在传统的农业管理模式中,牲畜的行为监测通常依赖于人工观测,耗时耗力且难以实现大规模实时监控。然而&#…...
Redis环境部署(主从模式、哨兵模式、集群模式)
一、概述 REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库。Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库…...
高阶C语言之六:程序环境和预处理
本文介绍程序的环境,在Linux下对编译链接理解,较为简短,着重在于编译的步骤。 C的环境 在ANSI C(标准C语言)的任何一种实现中,存在两个不同的环境。 翻译环境:在这个环境中,源代码…...
Vue 3 国际化 (i18n) 最佳实践指南
1. 安装依赖 npm install vue-i18n@9 2. 项目结构建议 src/ ├── i18n/ │ ├── index.ts # i18n 配置文件 │ ├── languages/ # 语言文件目录 │ │ ├── zh-CN.ts # 中文 │ │ ├── en-US.ts # 英文 │ │ └─…...
Acme PHP - Let‘s Encrypt
Lets Encrypt是一个于2015年三季度推出的数字证书认证机构,旨在以自动化流程消除手动创建和安装证书的复杂流程,并推广使万维网服务器的加密连接无所不在,为安全网站提供免费的SSL/TLS证书。 使用PHP来更新证书: Acme PHP | Rob…...
获取DOM 节点的四大方式
前言: 在 Vue 中,获取 DOM 节点可以通过多种方式,如自定义属性、ref 引用、类选择器和 ID 选择器等。 一、使用 ref 获取 DOM 实例 ref 是 Vue 中推荐的获取 DOM 节点的方式,它为每个节点分配一个唯一的引用,可以直…...
ROS2 Humble 机器人建模和Gazebo仿真
一.Ubuntu22.04系统虚拟机安装 1.下载镜像并安装 Index of /ubuntu-releases/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 2.安装选英文版,安装类型选清除磁盘。 3.遇到无法复制windows内容到虚拟机里。需安装VMware tools。VMware tools安装不了&a…...
docker容器镜像的制作、使用以及传递
目录 制作容器镜像使用Dockerfile制作镜像准备所需文件构建镜像怎么不使用基础镜像来构建容器镜像 使用容器镜像传递容器镜像 这篇文章讨论一下怎么使用docker制作容器镜像,容器镜像的使用,以及怎么传递容器镜像。 制作容器镜像 docker制作容器镜像推荐…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
