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制作容器镜像推荐…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
