分析C语言位域的访问开销

news2025/7/20 21:10:34

背景

C语言的位域用于描述结构体的指定字段占多少bit,使得多个字段可以存到一个字节里,也可以让一个字段占多个字节。它能减小结构体的内存占用,同时还能精确限定结构体字段的取值范围。

问题的提出

项目上有个硬件模块,它输入的是mesh结构体,输出的是小图结构体,软件根据小图计算出下一轮的mesh供硬件模块下一轮读入。
最近该模块为了节省内存,它将输出的数据弄成紧排(packed,每个字段不保证8bit对齐)的格式,这样要想访问就必须用到位域操作了
用到位域的结构体
因为数据量比较大,因此软件访问位域的开销不可忽略,需要定量分析下。

分析

想到用空的calc函数来对比分析,设计两个版本的calc函数,一个是pack版,一个是unpack版,函数的操作仅仅是将小图的两个字段赋值给mesh的两个字段(中间有什么截断移位全凭编译器决定),然后将两个函数反汇编,看各自对应多少条汇编指令,最后相减,就是访问位域的开销了。

测试代码

注意看,两个calc的C语言执行语句完全相同,差异只能是在汇编层面。

typedef struct _SmallPic_Pack {
    unsigned int gray:20;
    unsigned int dc:10;
    unsigned int reserved:2;
} SmallPic_Pack;

typedef struct _SmallPic_Unpack {
    unsigned int gray;
    unsigned short dc;
    unsigned short reserved;
} SmallPic_Unpack;

typedef struct _Mesh_Pack {
    unsigned int gray:16;
    unsigned int reserved1:4;
    unsigned int dc:10;
    unsigned int reserved2:2;
} Mesh_Pack;

typedef struct _Mesh_Unpack {
    unsigned short gray;
    unsigned short dc;
} Mesh_Unpack;

void calc_pack(SmallPic_Pack *pic, Mesh_Pack *mesh)
{
    mesh->gray = pic->gray;
    mesh->dc = pic->dc;
}

void calc_unpack(SmallPic_Unpack *pic, Mesh_Unpack *mesh)
{
    mesh->gray = pic->gray;
    mesh->dc = pic->dc;
}

int main() {
    SmallPic_Pack pic_pack = {0, 0, 0};
    SmallPic_Unpack pic_unpack = {0, 0, 0};
    Mesh_Pack mesh_pack = {0, 0, 0, 0};
    Mesh_Unpack mesh_unpack = {0, 0};
    calc_pack(&pic_pack, &mesh_pack);
    calc_unpack(&pic_unpack, &mesh_unpack);
    return 0;
}

测试结果

x86-64反汇编对比

000000000000066a <calc_pack>:
 66a:   55                      push   %rbp
 66b:   48 89 e5                mov    %rsp,%rbp
 66e:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
 672:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
 676:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 67a:   8b 00                   mov    (%rax),%eax
 67c:   25 ff ff 0f 00          and    $0xfffff,%eax
 681:   89 c2                   mov    %eax,%edx
 683:   48 8b 45 f0             mov    -0x10(%rbp),%rax
 687:   66 89 10                mov    %dx,(%rax)
 68a:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 68e:   0f b7 40 02             movzwl 0x2(%rax),%eax
 692:   66 c1 e8 04             shr    $0x4,%ax
 696:   66 25 ff 03             and    $0x3ff,%ax
 69a:   48 8b 55 f0             mov    -0x10(%rbp),%rdx
 69e:   66 25 ff 03             and    $0x3ff,%ax
 6a2:   c1 e0 04                shl    $0x4,%eax
 6a5:   89 c1                   mov    %eax,%ecx
 6a7:   0f b7 42 02             movzwl 0x2(%rdx),%eax
 6ab:   66 25 0f c0             and    $0xc00f,%ax
 6af:   09 c8                   or     %ecx,%eax
 6b1:   66 89 42 02             mov    %ax,0x2(%rdx)
 6b5:   90                      nop
 6b6:   5d                      pop    %rbp
 6b7:   c3                      retq

00000000000006b8 <calc_unpack>:
 6b8:   55                      push   %rbp
 6b9:   48 89 e5                mov    %rsp,%rbp
 6bc:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
 6c0:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
 6c4:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 6c8:   8b 00                   mov    (%rax),%eax
 6ca:   89 c2                   mov    %eax,%edx
 6cc:   48 8b 45 f0             mov    -0x10(%rbp),%rax
 6d0:   66 89 10                mov    %dx,(%rax)
 6d3:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 6d7:   0f b7 50 04             movzwl 0x4(%rax),%edx
 6db:   48 8b 45 f0             mov    -0x10(%rbp),%rax
 6df:   66 89 50 02             mov    %dx,0x2(%rax)
 6e3:   90                      nop
 6e4:   5d                      pop    %rbp
 6e5:   c3                      retq

可以看到,pack版比unpack多了9条指令

arm64反汇编对比

000000000040055c <calc_pack>:
  40055c:       d10043ff        sub     sp, sp, #0x10
  400560:       f90007e0        str     x0, [sp, #8]
  400564:       f90003e1        str     x1, [sp]
  400568:       f94007e0        ldr     x0, [sp, #8]
  40056c:       b9400000        ldr     w0, [x0]
  400570:       d3404c00        ubfx    x0, x0, #0, #20
  400574:       12003c01        and     w1, w0, #0xffff
  400578:       f94003e0        ldr     x0, [sp]
  40057c:       79000001        strh    w1, [x0]
  400580:       f94007e0        ldr     x0, [sp, #8]
  400584:       79400400        ldrh    w0, [x0, #2]
  400588:       d3443400        ubfx    x0, x0, #4, #10
  40058c:       12003c02        and     w2, w0, #0xffff
  400590:       f94003e1        ldr     x1, [sp]
  400594:       79400420        ldrh    w0, [x1, #2]
  400598:       331c2440        bfi     w0, w2, #4, #10
  40059c:       79000420        strh    w0, [x1, #2]
  4005a0:       d503201f        nop
  4005a4:       910043ff        add     sp, sp, #0x10
  4005a8:       d65f03c0        ret

00000000004005ac <calc_unpack>:
  4005ac:       d10043ff        sub     sp, sp, #0x10
  4005b0:       f90007e0        str     x0, [sp, #8]
  4005b4:       f90003e1        str     x1, [sp]
  4005b8:       f94007e0        ldr     x0, [sp, #8]
  4005bc:       b9400000        ldr     w0, [x0]
  4005c0:       12003c01        and     w1, w0, #0xffff
  4005c4:       f94003e0        ldr     x0, [sp]
  4005c8:       79000001        strh    w1, [x0]
  4005cc:       f94007e0        ldr     x0, [sp, #8]
  4005d0:       79400801        ldrh    w1, [x0, #4]
  4005d4:       f94003e0        ldr     x0, [sp]
  4005d8:       79000401        strh    w1, [x0, #2]
  4005dc:       d503201f        nop
  4005e0:       910043ff        add     sp, sp, #0x10
  4005e4:       d65f03c0        ret

可以看到,pack版只比unpack多了5条指令,看来arm这种寄存器多的CPU更适合多媒体类应用啊

测试总结

  1. 不论哪种CPU,pack版都比unpack版更耗CPU cycle,因为数据要8bit对齐才能计算或赋值,所以移位操作是无法避免的,硬件不做就得CPU做
  2. 横向比对x86和arm,发现unpack版二者所用指令差不多,但pack版明显arm指令更少。

总结

访问位域有开销,这包括移位、截断等操作,但考虑到内存局部性原理,这些操作都不影响cache,因此性能并不会差多少。

后记

位域在定义时,先定义的字段在低bit,然后逐渐往高bit放,别弄反了。这有个好处,就是不论是8位CPU还是64位CPU,字段的位置都跟定义顺序保持一致。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统下基于Docker安装Yapi,并且迁移Yapi数据

本文主要讲四个部分&#xff1a; 1.什么是Yapi 2.Centos7 下基于docker安装Yapi 3.Yapi数据迁移 4.利用Nginx反向代理 什么是Yapi YApi 是高效、易用、功能强大的 api 管理平台&#xff0c;旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、…

网络安全-内网DNS劫持-ettercap

网络安全-内网DNS劫持-ettercap 前言 一&#xff0c;我也是初学者记录的笔记 二&#xff0c;可能有错误的地方&#xff0c;请谨慎 三&#xff0c;欢迎各路大神指教 四&#xff0c;任何文章仅作为学习使用 五&#xff0c;学习网络安全知识请勿适用于违法行为 学习网络安全知识请…

【C++、数据结构】哈希 — 闭散列与哈希桶的模拟实现

文章目录&#x1f4d6; 前言1. STL中哈希表的两个应用⚡1.1 &#x1f31f;unordered_set1.2 &#x1f31f;unordered_map2. 常见查找的性能对比&#x1f4a5;3. 哈希表模拟实现&#x1f3c1;3.1 哈希的概念&#xff1a;3.2 哈希函数&#xff1a;3.3 哈希冲突&#xff1a;3.4 闭…

RPC概念与理解(一)

目录 1.写在前面 2. 电商系统的演变 2.1 单一应用框架 2.2 垂直应用框架 2.3 分布式应用架构(RPC) 2.4 流动计算架构(SOA) 2.5 架构演变详解 3.远程调用方式 RPC Http 总结&#xff1a;对比RPC和http的区别 4.什么是PRC 5.RPC是什么 6.RPC剖析 6.1.RPC的工作原理…

Java并发编程解析之基于JDK源码解析Java领域中ReentrantLock锁的设计思想与实现原理

一、开头 在并发编程领域&#xff0c;有两大核心问题&#xff1a;一个是互斥&#xff0c;即同一时刻只允许一个线程访问共享资源&#xff1b;另一个是同步&#xff0c;即线程之间如何通信、协作。 主要原因是&#xff0c;对于多线程实现实现并发&#xff0c;一直以来&#xff…

【蓝桥杯集训3】二分专题(3 / 5)

目录 二分模板 1460. 我在哪&#xff1f; - 二分答案 哈希表 1221. 四平方和 - 哈希表 / 二分 1、哈希表 2、二分 自定义排序 1227. 分巧克力 - 113. 特殊排序 - 二分模板 l r >> 1 —— 先 r mid 后 l mid1 —— 寻找左边界 —— 找大于某个数的最小值lr…

SRV6跨域优势

背景 运营商网络作为一张覆盖全国的网络&#xff0c;其主体分为骨干&#xff0c;省干&#xff0c;城域网层级&#xff0c;主流的管理模式为分层级管理。随着运营商网络服务的终端规模不断增长&#xff0c;不同地理位置之间网络连接的需求变得非常的普遍&#xff0c;但不同网络…

SpringMVC使用JSTL

简介 JSTL 标签是一个开放源代码的 JSP 标签库&#xff0c;是由 apache 的 jakarta 小组来维护的&#xff1b;JSTL 只能运行在支持 JSP1.2 和Servlet2.3 规范的容器上&#xff0c;在 jsp 页面中经常用到&#xff0c;能帮助我们实现一些特殊的功能&#xff0c;例如&#xff1a;…

windows-Mysql的主从数据库同步设置

复制原有的mysql修改my.ini配置文件 修改端口号修改从数据的地址和从数据库的数据存放地址安装从数据库进入从数据库的bin目录&#xff0c;打开命令窗口输入命令&#xff1a;mysqld.exe install mysql-back --defaults-file "C:\ProgramData\MySQL\MySQL Server 5.7-back\…

1、创建第一个Android项目

1.1、创建Android工程项目&#xff1a;双击打开Android Studio。在菜单栏File中new-->new project3、在界面中选择Empty Activity&#xff0c;然后选择next4、在下面界面中修改工程名称&#xff0c;工程保存路径选择java语言&#xff0c;然后点击finishAndroid studio自动为…

实现了统一消息中心的微服务基础框架 JVS,快点赞收藏

一、开源项目简介基于JVS&#xff08;基于spring cloud封装&#xff09;的基础开源框架&#xff0c;实现了基于多对多租户能力的管理系统。二、基础框架实现功能支持数据管理支持分布式定时任务支持分布式日志采集支持系统监控支持动态配置中心支持模板消息支持链路跟踪支持邮件…

jenkins部署指定任意版本和配置详细教程 jenkins 2.361版本示例

Jenkins构建CI/CD什么是CI/CD&#xff1a;持续集成/持续发布---开发(git) -->git主库-->jenkins(gitjdktomcatmaven打包测试&#xff09;-->发布到tomcat服务器。持续集成(Continuous Integration, CI): 代码合并&#xff0c;构建&#xff0c;部署&#xff0c;测试都在…

【Airplay_BCT】Bonjour 和本地链接、域名和 DNS

Bonjour 零配置网络架构支持在局域网或广域网上发布和发现基于 TCP/IP 的服务。本文档概括介绍了 Bonjour 架构&#xff0c;并简要介绍了可用的 Bonjour API。 Bonjour 是 Apple 对一套零配置网络协议的实现。 Bonjour 旨在让用户更轻松地进行网络配置。 例如&#xff0c;Bon…

ChatGPT一路狂飙,NVMe SSD能否应对性能挑战?

近日&#xff0c;ChatGPT持续火爆&#xff0c;用户在短短两个月内迅速破亿&#xff0c;大量用户涌入导致ChatGPT访问和数据规模指数级增加。与数月前发布的版本相比&#xff0c;新的ChatGPT“智能”了许多&#xff0c;不仅可以像人类一样聊天交流&#xff0c;甚至能够完成一定程…

70. with open( ) as 以自动关闭文件的方式打开文件

70. with open( ) as 以自动关闭文件的方式打开文件 文章目录70. with open( ) as 以自动关闭文件的方式打开文件1. 知识回顾1. open函数语法参考2. 准备工作3. mode模式知识回顾2. with open 语句的作用3. with open 语句语法4. with open 语句实操5. 总结1. 代码总结2. 重点知…

Pandas学习2

Pandas学习2 None 两种缺失数据&#xff1a;None、np.nan(NaN) None是python自带的&#xff0c;不能参与计算。类型是object np.nan 浮点类型&#xff0c;说明能参与计算&#xff0c;但是结果是nan pandas中的None和np.nan pandas中的None和np.nan都视为np.nan 如果列…

ArkUI新能力,助力应用开发更便捷

ArkUI是一套构建分布式应用的声明式UI开发框架。它具备简洁自然的UI信息语法、丰富的UI组件、多维的状态管理&#xff0c;以及实时界面预览等相关能力&#xff0c;帮助您提升应用开发效率&#xff0c;并能在多种设备上实现生动而流畅的用户体验。随着HarmonyOS 3.1版本的发布&a…

签收后再补录物流信息单号要如何分析

随着网络时代电商平台掘起&#xff0c;购物平台也是越来越多&#xff0c;呈现出快递也是越来越多&#xff0c;电商平台和快递两者是密不可分&#xff0c;有下单就需要快递物流&#xff0c;所以快递公司也是很重要一个环节&#xff0c;最近有朋友向咨询如何分析出比如单号过已签…

Springboot扩展点之SmartInitializingSingleton

前言这篇文章会重点分析一下SmartInitializingSingleton扩展点的功能 特性、实现方式 、工作原理。SmartInitializingSingleton扩展点内只有一个扩展方法&#xff0c;且执行时机在Spring Bean的生命周期里比较靠后&#xff0c;很重要&#xff0c;但是也很简单。功能特性1、Smar…

0基础入行Java开发—详解Java泛型之详解通配符

今天我们来继续讲解泛型中另一个非常重要的概念&#xff0c;就是那个“小问号”——通配符! 通配符概念 泛型中除了用 表示泛型外&#xff0c;还有 <?>这种形式。&#xff1f; 被称为通配符。那么引入通配符的原因又是什么呢&#xff1f;看下面这段代码&#xff1a; …