当前位置: 首页 > news >正文

C#多维数组不同读取方式的性能差异

背景

近来在优化一个图像显示程序,图像数据存储于一个3维数组data[x,y,z]中,三维数组为一张张图片数据的叠加而来,其中x为图片的张数,y为图片行,Z为图片的列,也就是说这个三维数组存储的为一系列图片的数据集,整个数据集构成了一个现实中的物体(其实就是一个人的某部位CT扫描结果)。

yoz为主视图,xoz为俯视图,xoy为左视图。

在进行数组读取时,发现如果X固定,YZ为变量获取一张图片时(Y从小到大,Z从小到大),整个获取像素的函数运行时间一般为3ms左右;若Y固定,XZ为变量获取俯视图的图处理时,整张图片的获取也差不多在3ms左右;但若Z固定,获取左视图时,则获取整张图片的时间至少为主视图或俯视图的2倍,最大可能为10倍甚至更多。

原因探索——为何Z固定时,获取整张图片的时间会增大?

多维数组的遍历

首先了解一下多维数组的遍历,遍历元素的方式为:首先递增最右边维度的索引,然后是它左边的一个维度,以此类推,向最左的索引遍历元素。

那么对于一个3*3*3的三维数组,X固定获取的获取,就完全是在一个连续的区域获取,如下所示,对于3*3*3的三维数组,X固定3个区域均为连续在一块儿。

Y固定也是类似,只是相对有些分散,但仍是几个连续的区域,如下所示,Y为0时的黄色区域,Y为1时的白色区域,Y为2时的棕色区域。

Z固定时,看上去也是连续的区域,读取时间应该不会相差太多,如下图所示:

数组在存储单元中存储

存储单元是一维的结构,那么多维数组就需要约定数组的存储次序,C#中数组是以行序为主序(不同语言实现不同)的存储结构,也就是如前面图中3维数组的显示一样以行优先进行存储数据,一行一行将数据存储存储到存储单元。

那么数组的读取,读取X为定值的所有值,就只要知道X固定的第一个值位置,然后顺序读取X为定值的整个区间就可以了;Y固定时与此类似,只是区间会变多;而Z固定就完全不一样了,以前述3*3*3的3维数组为例,Z为1时所有数据,都不是连续的,也就是说它相应的存储位置都需要重新去计算,那么它的读取时间肯定就是最长的了。

如何优化?

那么,才能提高3维数组的读取时间呢?了解了产生性能问题的原因,针对此可以用以下方法进行一定的性能提升。

1. 循环展开

关于循环展开的详细解释,可自行查找相应资料,本文不作赘述。

循环展开代码如下:

展开前:

  for (int i = 0; i < 4; ++i){var pixelDataindex = y * rowStride + x * 4 + i;if (i != 3)PixelData[pixelDataindex] = (byte)HUtoGraysale(ctData16ct[x, y, (int)frameIndex], slope, intercept, imgHeight, imgWidth);elsePixelData[pixelDataindex] = 255;}

展开后:

var pixelDataindex = y * rowStride + x * 4;
var gray = (byte)HU2Graysale(ctData16ct[x, y, index], slope, intercept, high, low, imgWidth);
UpdatePixelData(PixelData, pixelDataindex, gray);

展开后涉及的 UpdatePixelData如下:

private static void UpdatePixelData(byte[] PixelData, int pixelDataindex, byte gray)
{PixelData[pixelDataindex] = gray;PixelData[pixelDataindex + 1] = gray;PixelData[pixelDataindex + 2] = gray;PixelData[pixelDataindex + 3] = 255;
}

通过前后对比测试结果来看,循环展开后的速度比展开前提高了50%。

2. 多线程

在1中对最内层循环进行了循环展开,在最内层循环的外层循环可以将循环的区域拆分为多个,然后每个区域用一个线程来进行相应的计算,最后将整个计算结果返回即可。

此处注意,并不是拆越多用越多的线程越好,一般不要超过CPU的核心数。

3. 减少不必要的操作

如减少循环中的强制转换,装箱拆箱,以及一些可以转到外边的计算。

4. 优化数据结构

如果性能非常敏感,可以考虑针对你需要的那一维数据进行特殊存储。如本文开头提到的data[x,y,z],可以将Z存储到x的位置,x存储到Z的位置,那么data[z,y,x]的读取速度就会有大的提升。

以上都是在硬件固定的情况下说的,当然,有更好的硬件环境肯定是能提高执行速度的。

参考链接

数组 - C# reference | Microsoft Learn

改几行代码,for循环耗时从3.2秒降到0.3秒!真正看懂的都是牛人!

参考书籍:

数据结构(C语言版)(第2版)——严蔚敏 李冬梅 吴伟民

相关文章:

  • 快手发布大模型产品“可图”,超20种创新AI图像玩法限免上线
  • React-useState
  • 经典获奖案例 | 度小满互联网金融开源软件治理解决方案
  • JVM 虚拟机
  • 10. RBAC权限管理从零到一实现(一)
  • 【学习笔记】数据结构(一)
  • spring 优雅替换bean
  • HTML静态网页成品作业(HTML+CSS)—— 冶金工程专业展望与介绍介绍网页(2个页面)
  • SQL—DQL之执行顺序(基础)
  • Java语言编程考试难吗:深入剖析与应对策略
  • windows11家庭版、专业版、工作站版区别
  • 利用 Docker 简化Redis部署:快速搭建Redis服务
  • webserver服务器从零搭建到上线(八)|EpollPoller事件分发器类
  • 南澳葡萄酒发展论坛盛邀国际荐酒师香港协会共商开放关税中国发展
  • 【计算机毕业设计】基于SSM++jsp的在线云音乐系统【源码+lw+部署文档】
  • 自己简单写的 事件订阅机制
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • Angular6错误 Service: No provider for Renderer2
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • github指令
  • IDEA常用插件整理
  • java2019面试题北京
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • ucore操作系统实验笔记 - 重新理解中断
  • 阿里云Kubernetes容器服务上体验Knative
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 设计模式走一遍---观察者模式
  • 使用parted解决大于2T的磁盘分区
  • 无服务器化是企业 IT 架构的未来吗?
  • 移动端 h5开发相关内容总结(三)
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • ###C语言程序设计-----C语言学习(3)#
  • #pragma pack(1)
  • #window11设置系统变量#
  • (C++)八皇后问题
  • (function(){})()的分步解析
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (十一)c52学习之旅-动态数码管
  • (四) Graphivz 颜色选择
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转载)Linux 多线程条件变量同步
  • .equals()到底是什么意思?
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .net 调用海康SDK以及常见的坑解释
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .NET/ASP.NETMVC 大型站点架构设计—迁移Model元数据设置项(自定义元数据提供程序)...
  • .net访问oracle数据库性能问题
  • .sys文件乱码_python vscode输出乱码
  • /proc/vmstat 详解
  • []AT 指令 收发短信和GPRS上网 SIM508/548
  • [1525]字符统计2 (哈希)SDUT
  • [20180312]进程管理其中的SQL Server进程占用内存远远大于SQL server内部统计出来的内存...