IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 行优先与列优先遍历opencv中Mat数据类型的效率差异 -> 正文阅读

[人工智能]行优先与列优先遍历opencv中Mat数据类型的效率差异

一、数组在内存的存储方式

数组是数据结构的基础,之所以这么说是因为数组反映了内存的物理结构。在内存中,数组是连续分布的。而在程序中,往往要在内存中分配一块连续的空间来使用。例如,在图像处理邻域,耳熟能详的opencv中有一数据类型Mat,我们一般都会以Mat来存储图像数据。Mat是一个二维数组,可以通过两个for循环遍历图像上各个像素值。有人习惯按行优先遍历,有人喜欢按列优先遍历,表面上看,这两中写法的时间复杂度都是O(nm)(n表示图像的高,m表示图像的宽),可实际上是否都是O(nm)呢?

二、代码示例及结果

列优先遍历代码如下(示例):

	Mat src = imread("image0.jpg", IMREAD_GRAYSCALE);
	if (!src.empty())
		imshow("src", src);
	int nums = 10;
	while (nums)
	{
		double t = (double)getTickCount();
		for (int i = 0; i < src.cols; i++)
		{
			for (int j = 0; j < src.rows; j++)
			{
				uchar srcValue = src.at<uchar>(j, i);
			}
		}
		t = ((double)getTickCount() - t) / getTickFrequency(); //获得时间,单位是秒
		cout << "time:" << t<<"ms"<<endl;
		nums--;
	}

通过一个循环,重复测试了10次,列优先遍历的时间耗时通过控制台打印如下:
在这里插入图片描述
行优先遍历代码如下(示例):

	Mat src = imread("image0.jpg", IMREAD_GRAYSCALE);
	if (!src.empty())
		imshow("src", src);
	int nums = 10;
	while (nums)
	{
		double t = (double)getTickCount();
		for (int i = 0; i < src.rows; i++)
		{
			for (int j = 0; j < src.cols; j++)
			{
				uchar srcValue = src.at<uchar>(i, j);
			}
		}
		t = ((double)getTickCount() - t) / getTickFrequency(); //获得时间,单位是秒
		cout << "time:" << t<<"ms"<<endl;
		nums--;
	}

同样,行优先遍历也通过一个循环,重复测试了10次,时间耗时通过控制台打印如下:
在这里插入图片描述
通过以上测试可以发现,行优先遍历的方式比列优先效率快。

三.分析

1. CPU高速缓存(英语:CPU Cache):在计算机系统中,CPU高速缓存是用于减少处理器访问内存所需平均时间的部件。当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。缓存之所以有效,主要是因为程序运行时对内存的访问呈现局部性(Locality)特征。这种局部性既包括空间局部性(Spatial Locality),也包括时间局部性(Temporal Locality)。有效利用这种局部性,缓存可以达到极高的命中率。(百度百科解释)。
2.虚拟内存:虚拟内存被操作系统用以管理内存,对物理内存进行扩展,其作用是替代物理内存的部分工作来运行程序,让操作系统就可以运行更多的程序,同时执行更多的任务。
3.二维数组在内存中的存储结构如下图:数据在内存中是以行优先的方式存储。

在这里插入图片描述
4.代码测试的图像大小为4096X1200,位深度为8,如下图。之所以行优先遍历的方式比列优先效率快,是因为cpu在访问内存地址的时候,首先访问的是虚拟内存,检查TLB,映射成对应的物理地址进行数据访问。如果命中,会得到其物理地址,之后会访问cache,如果cache中有要访问的数据,那么本次访问就结束,如果没有,就到内存中寻找,并更新cache;如果TLB不命中,那么那么系统内核会调用缺页异常处理程序去处理,这个过程中会进行页替换等操作,最终取得要访问的数据。而内存的物理地址中,二维数组是以行优先的顺序存储,测试图像数据大小为4096X1200字节,假设内存页为4096字节,那么按行优先遍历,遍历完一行则中断一次缺页,整个过程只需要中断1200次缺页异常,进行页替换。而按列优先遍历,每遍历一个元素便中断一次缺页异常,整个过程4096X1200次中断,可想而知,按列优先遍历产生中断耗时比按行优先遍历大了许多。而实际中物理内存一般比较充足,系统分配的页内存远远不止4096字节,所以缺页中断的次数不会这么多。同理,如果检查TLB命中,得到其物理地址之后会访问cache,如果cache没有要访问的数据,就到内存中寻找,并更新cache;由于二维数组在内存中的存储方式,按列优先遍历访问cache的命中率比按行优先遍历的低。
在这里插入图片描述

综上所述:行优先遍历的方式比列优先效率快。

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-09-02 11:21:53  更:2021-09-02 11:22:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 16:22:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码