二维数组在内存中的行存储和列存储

目录

例题:

0. BaseAddress

1. 行存储方式(Row-major order)

2. 列存储方式(Column-major order)

3. 解方程找到 i 和 j

行存储和列存储方式的区别

行存储方式(Row-major order):

列存储方式(Column-major order):

优缺点

行存储方式的优点:

行存储方式的缺点:

列存储方式的优点:

列存储方式的缺点:

行存储方式的应用场景

列存储方式的应用场景

混合存储方式

另一道计算例题:

从填充的角度去理解上道题:


例题:

  二维数组 N 的元素是 4 个字符(每个字符占一个存储单元)组成的串,行下标 i 的范围从 0 到 4,列下标 j 的范围从 0 到 5,N 按行存储时元素 N[3][5]的起始地址与 N 按列存储时元素( )的起始地址相同。

我们需要理解二维数组在内存中的行存储和列存储方式,并找到对应元素的地址。

0. BaseAddress

  在内存中,BaseAddress 是数组的起始地址,即数组中第一个元素(通常是 N[0][0])的内存地址。所有数组元素的地址计算都是基于这个起始地址进行的。

1. 行存储方式(Row-major order)

  在行存储方式中,数组的元素是按行顺序存储的。对于数组 N[5][6](行数为5,列数为6),元素 N[i][j] 在内存中的位置可以通过下面的公式计算: N[i][j] =BaseAddress + (i *列数+ j) *元素大小  其中,元素大小是由于每个元素由4个字符组成,所以是 4 字节。

对于 N[3][5],地址计算为:

 N[3][5] = BaseAddress + (3 * 6 + 5) * 4 

 N[3][5] = BaseAddress + 23 * 4 

 N[3][5] = BaseAddress + 92 

2. 列存储方式(Column-major order)

在列存储方式中,数组的元素是按列顺序存储的。对于同样的数组 N[5][6],元素 N[i][j] 的地址计算公式变为:

 N[i][j] = BaseAddress + (j * 行数 + i) * 元素大小

我们需要找到一个元素 N[i][j],使得它在列存储方式中的地址与行存储方式中 N[3][5] 的地址相同

 N[i][j] = BaseAddress + (j * 5 + i) * 4 = BaseAddress + 92 

 (j * 5 + i) * 4 = 92  ===>  (j * 5 + i) = 23

3. 解方程找到 i 和 j

我们需要找到满足 (j * 5 + i) = 23 的 i 和 j 的整数解,其中 i 的范围是 0 到 4,j 的范围是 0 到 5。通过试验或解方程,我们找到:

  • 当 j = 4 时,( 4 * 5 + i = 23 ) 解得 ( i = 3 )。

因此,N[3][4] 是按列存储时与按行存储的 N[3][5] 起始地址相同的元素。

行存储和列存储方式的区别

  1. 行存储方式(Row-major order):

    • 数组元素按行顺序存储在内存中。
    • 对于二维数组 N[i][j],元素 N[i][j] 的地址计算公式为: N[i][j] =BaseAddress + (i *列数+ j) *元素大小
    • 这是 C/C++、Java、Python 等多数编程语言采用的默认方式
  2. 列存储方式(Column-major order):

    • 数组元素按列顺序存储在内存中。
    • 对于二维数组 N[i][j],元素 N[i][j] 的地址计算公式为: N[i][j] = BaseAddress + (j * 行数 + i) * 元素大小
    • 这是 Fortran 和 MATLAB 等语言采用的方式。

优缺点

行存储方式的优点:

  • 局部性优化:在处理行相关的数据时,由于数据连续存储,可以更好地利用 CPU 缓存,提高访问效率。
  • 简单直观:与多数编程语言的默认数组遍历顺序一致,编程时较为直观。

行存储方式的缺点:

  • 在需要频繁访问列数据的情况下,可能会导致较多的缓存未命中,降低性能。

行存储方式的主要缺点通常是在需要频繁访问列数据的场景中性能降低。这种性能下降主要由以下几个因素引起:

  1. 缓存未命中(Cache Misses):

    • 现代计算机系统使用缓存来减少访问主存储器的时间。缓存工作基于局部性原理,包括时间局部性和空间局部性。行存储方式优化了对行数据的空间局部性,因为连续的行数据被存储在连续的内存地址中。
    • 然而,当访问列数据时,数据元素在内存中是间隔存放的。例如,在二维数组中,访问同一列的下一个元素需要跳过整行的元素。这种存储方式导致每次列数据访问可能都会引发缓存未命中,因为需要的数据并不在缓存中连续存储。
  2. 内存带宽利用不足:

    • 当处理器从内存中读取数据时,它通常会一次读取一个“字块”(word block),这是一定数量的连续字节。在行存储中,如果执行的是列操作,那么每次内存读取可能只用到很少的相关数据,而其余数据则被浪费,这导致内存带宽利用率低下。
  3. 预取策略的效率降低:

    • 现代处理器使用预取策略来预测接下来可能需要的数据,并提前从内存中加载到缓存中。行存储方式在进行列访问时,预取器可能难以正确预测接下来需要哪些数据,因为这些数据在内存中不是连续的。
  4. 多线程和并行处理的复杂性增加:

    • 在多线程环境中,如果多个线程需要访问同一数组的不同列,行存储方式可能导致更频繁的缓存行冲突和无效化,这会降低并行执行的效率。

列存储方式的优点:

  • 优化列操作:在需要频繁处理列数据的应用中(如某些数学和统计操作),可以更有效地利用 CPU 缓存,提高性能。

列存储方式的缺点:

  • 在处理行数据时可能效率不如行存储方式。

行存储方式的应用场景

  1. 游戏开发:

    • 在游戏开发中,经常需要快速访问和更新对象的属性,如位置、速度等,这些属性通常在对象的数据结构中按行顺序存储。行存储方式可以快速加载整个对象的数据,优化性能。
  2. 应用程序开发:

    • 许多应用程序,如客户管理系统或电子商务系统,需要处理大量的事务数据,这些数据通常按行进行访问和更新。行存储方式使得这些操作更为高效。
  3. 传统关系数据库:

    • 大多数关系数据库系统(如 MySQL, PostgreSQL)在处理交易型查询时使用行存储,因为这些查询通常涉及到多个列的少量行。

列存储方式的应用场景

  1. 大数据分析和数据仓库:

    • 在数据仓库和大数据分析中,列存储方式允许快速读取大量数据的特定列,这对于执行统计分析和报告非常有用。例如,如果一个分析师只需要访问销售数据中的“销售额”和“日期”列,列存储可以大幅减少需要读取的数据量。
  2. 科学计算和工程应用:

    • 在科学计算中,经常需要对大型矩阵进行列操作,如矩阵乘法和其他线性代数运算。列存储方式可以提高这些操作的效率。
  3. 现代列式数据库:

    • 一些现代数据库系统,如 Apache Cassandra 和 Google Bigtable,采用列存储方式来优化读取大量数据的特定列的性能,特别适用于读密集型的应用场景。

混合存储方式

  一些现代系统甚至采用混合存储方式,结合行存储和列存储的优点,以适应多变的数据访问模式。例如,南大通用 GBase数据库和Microsoft SQL Server 的某些版本就支持这种混合模式,可以根据应用需求动态优化数据存储和访问。

  总结来说,选择行存储还是列存储取决于数据访问模式。了解和选择合适的存储方式可以显著影响程序的性能和效率。

另一道计算例题:

  设有数组 A[i,j],数组的每个元素长度为 3 字节,i 的值为 1 到 8,j 的值为 1 到 10,数组从内存首地址 BA 开始顺序存放,当以列为主序存放时,元素 A[5,8]的存储首地址是 BA+?

  此数组是一个8行10列的数组,元素大小为3字节,但i,j分别都是从1开始计数的。

  如果死套公式,列存储:

  A[i][j] = A[5][8] = BA + (j * 行数 + i) * 元素大小 = BA + (8*8+5)*3 = BA + 69*3 = BA + 207。就会得到错误答案

 我们的来计算元素 A[5,8] 的地址是基于列主序存储的正确公式,但是在计算过程中有一点小错误需要纠正。公式本身是正确的,但应用时需要注意细节。让我们一步步来检查和应用这个公式:

 在此题中,正确公式是:A[i][j] = BaseAddress + ((j-1)* 行数 + (i-1)) * 元素大小

这里的 (i) 和 (j) 是从1开始的索引,因此要计算以0为基的索引我们需要从 (i) 和 (j) 中分别减去1。

对于元素 A[5,8],使用以下参数:

  • (i = 5)  i-1=4
  • (j = 8)  j-1=7
  • 行数 = 8(因为 (i) 的范围是1到8)
  • 元素大小 = 3 字节
  • 将这些值代入公式中  A[5][8] = BA + (7 * 8 + 4) * 3 = BA + 180字节。

  因为我们是从1开始计数的,而在计算内存地址时需要以0为基数。这样的细节在处理数组和内存地址计算时非常重要。

从填充的角度去理解上道题:

  在列主序存储中,数组的元素是按列填充的,也就是说,首先填充第一列的所有行,然后是第二列的所有行,依此类推

  给定的数组 A[i, j] 的维度是 i = 1 到 8 和 j = 1 到 10,每个元素占用 3 字节。我们需要找到元素 A[5,8] 的存储首地址。

  按列主序,第一列的元素 A[1,1], A[2,1], ..., A[8,1] 都先被存储,每个元素占用 3 字节。因此,第一列占用的总字节数是 8 * 3 = 24 字节。同理,每列都占用 24 字节

  元素 A[5,8] 位于第 8 列,第 5 行。要找到这个元素的地址,我们需要先计算前 7 列的总字节数,然后加上到第 5 行的偏移量

  1. 前 7 列占用的字节总数 = 7 列 × 24 字节/列 = 168 字节
  2. 第 8 列中,前 4 行占用的字节 = 4 行 × 3 字节/行 = 12 字节

因此,元素 A[5,8] 的存储首地址 = 基地址 BA + 168 字节 + 12 字节 = BA + 180 字节。

所以,元素 A[5,8] 的存储首地址是 BA + 180 字节。

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

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

相关文章

逆向案例二十八——红某点集登录接口逆向序

网址:aHR0cHM6Ly93d3cuaHJkanl1bi5jb20vIy9sb2dpbj9yZWRpcmVjdD0lMkZyZWFsVGltZUxpdmluZw 登录接口,发现两个参数加密,分别是pwd和sig,t很明显是时间戳。 观察pwd,发现很像md5加密,我输入的密码是123456,在在线加密网…

Oracle SQL - HAVING和分析函数的执行顺序

分析函数是基于最终的结果集进行开窗的,所以HAVING比分析函数先执行 ↓ 没有HAVING时,MAX(col3) over()是A2 SQL> WITH subq_a AS2 (SELECT A col1, A1 col2, 10 col33 FROM dual4 UNION ALL5 SELECT A col1, A1 col2, -5 col36 F…

day81 session会话 文件上传

知识点: session 文件上传 一 session 1)session:会话 在服务器端存储信息 指客户与服务器的会话 当用户通过浏览器访问服务器的某个页面时,在服务器开辟一个内存空间session 每个session 有唯一的id 2)session过期 …

C——文件操作

1.前言 为什么要使用文件呢? 文件是储存在电脑的磁盘中的,如果没有文件,我们写程序的数据就会存储在电脑的内存中,程序退出,操作系统就会收回内存,数据就丢失了等再次运行程序的时候,是看不到…

【春秋云镜】CVE-2023-43291 emlog SQL注入

靶场介绍 emlog是一款轻量级博客及CMS建站系统,在emlog pro v.2.1.15及更早版本中的不受信任数据反序列化允许远程攻击者通过cache.php组件执行SQL语句。 不感兴趣的可以直接拉到最后面,直接获取flag 备注:没有通过sql注入获取到flag&…

汇编语言——将DX,AX组成的32位数逻辑左移3位

data segment data ends stack segment stacktop label worddw 100 dup (?) stack ends code segmentassume cs:code,ds:data,ss:stack main proc farmov ax,datamov ds,axmov ax,stackmov ss,axlea sp,top;0000 0001 1100 1010 | 0000 0010 0001 1111;逻辑左移三位后&#xf…

基于SpringBoot框架的智慧食堂

采用技术 基于SpringBoot框架实现的web的智慧社区系统的设计与实现~ 开发语言:Java 数据库:MySQL 技术:SpringBootMyBatis 工具:IDEA/Ecilpse、Navicat、Maven 页面展示效果 系统功能 系统首页 用户注册页面 菜品信息页面 …

spispio

SS:NSS( NOT SLAVE SELET )、CS( CHIP SELECT ) 同步:SCK引脚用于提供时钟信号,数据位的输出和输入都是在SCK的上升沿或下降沿进行,数据位的收发时刻因此得以确定。同步时序下时钟快慢或中途暂停都没问题。SCK相当于IIC通信下的SCL&#xff0…

Spark面试整理-如何在Spark中进行数据清洗和转换?

在Apache Spark中进行数据清洗和转换是数据处理流程中的一个重要步骤。这个过程涉及从原始数据中删除或修正不准确、不完整、不相关或不正确格式的数据,以及将数据转换成更适合分析的形式。以下是在Spark中进行数据清洗和转换的一些常见方法: 1. 读取数据 首先,需要从支持的…

Android 性能优化(七):APK安装包体积优化

包体积优化重要性 移动 App 特别关注投放转化率指标,而 App 包体积是影响用户新增的重要因素,而 App 的包体积又是影响投放转化率的重要因素。 Google 2016 年公布的研究报告显示,包体积每上升 6MB 就会带来下载转化率降低 1%, …

SpringBoot Jar包在CentOS7.x上注册成服务并开机启动

本文将介绍如何把SpringBoot Jar包在CentOS7.x上注册成服务并设置为开机启动。 在CentOS系统中,将Spring Boot应用程序打包成jar文件后,可以通过创建Systemd服务来将其部署为系统服务,并设置为开机启动。 以下是详细的步骤和说明&#xff1a…

直方图与核密度估计

技术背景 直方图是一种经常被用于统计的图形表达形式,简单来说它的功能就是用一系列的样本数据,去分析样本的分布规律。而直方图跟核密度估计(Kernel Density Estimation,KDE)方法的主要差别在于,直方图得到的是一个离散化的统计分…

【全开源】多功能完美运营版商城 虚拟商品全功能商城 全能商城小程序 智慧商城系统 全品类百货商城

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 完美运营版商城/拼团/团购/秒杀/积分/砍价/实物商品/虚拟商品等全功能商城 干干净净 没有一丝多余收据 还没过手其他站 还没乱七八走的广告和后门 后台可以自由拖曳修改前端UI页面 …

实名制重要性、PHP身份实名认证示例、身份证ocr识别核验

身份证丢失失,可能会被不法分子利用去贷款。虽然是被人冒名办理,客观上不承担责任,但会造成个人信用信息的困扰。因此,对于个人来讲,要妥善保管自己的身份证,避免不必要的麻烦。对于贷款机构来说&#xff0…

Aigtek功率放大器的使用方法有哪些

功率放大器是一种将小信号放大为大信号的电子设备,广泛应用于无线通信、音频系统、雷达等领域。在使用功率放大器时,需要注意以下几个方面: 电源供应:功率放大器需要提供稳定的电源供应以保证正常工作。通常情况下,功率…

正式发布的Spring AI,能让Java喝上AI赛道的汤吗

作者:鱼仔 博客首页: https://codeease.top 公众号:Java鱼仔 前言 最近几年AI发展实在太快了,仿佛只要半年没关注,一个新的大模型所产生的效果就能超越你的想象。Java在AI这条路上一直没什么好的发展,不过Spring最近出来了一个新的模块叫做S…

[Linux][进程间通信][一][匿名管道][命名管道]详细解读

目录 0.进程间通信?1.进程间通信目的2.进程间通信分类3.进程间通信的本质理解 1.什么是管道?2.匿名管道1.认识函数2.如何让不同的进程,看到同一份资源?3.用fork来共享管道原理4.站在文件描述符角度 -- 深刻理解管道5.站在内核角度…

目标检测——食品饮料数据集

一、重要性及意义 对食品和饮料进行目标检测的重要性和意义体现在多个方面: 商业应用与市场分析:目标检测技术在食品和饮料行业有着广泛的应用前景。通过对超市货架、餐馆菜单或广告海报中的食品和饮料进行自动识别和计数,商家可以获取关于产…

【微服务】spring状态机模式使用详解

一、前言 在很多系统中,通常会涉及到某个业务需要进行各种状态的切换操作,例如在审批流程场景下,某个审批的向下流转需要依赖于上一个状态的结束,再比如电商购物场景中,一个订单的生命周期往往伴随着不同的状态,比如待支付,支付完成,已发货等等,状态的存在,让一个业…

登录解析(后端)

调试登录接口 进入实现类可以有 验证码校验 登录前置校验 用户验证 验证码校验 通过uuid获取redis 中存储的验证码信息,获取后对用户填写的验证码数据进行校验比对 用户验证 1.进入控制器的 /login 方法 2.进入security账号鉴权功能,经过jar内的流…