优化程序中的数据:从数组到代数

前言

我们往往都希望优化我们的程序,使之达到一个更好的效果,程序优化的一个重点就是速度,加快速度的一个好办法就是使用并行技术,但是,并行时我们要考虑必须串行执行的任务,也就是有依赖关系的任务,任务中的重点往往是具体的数据,这些任务中的数据通常具有局部性和关联性。

而数据中数组具有代表性,现在,让笔者从数组开始,谈谈程序数据的优化。

从数据的存储内存开始

我们都知道计算机的基本内存结构如下:

处理器
cache高速缓存
多层cache
总线
内存

而内存的结构又可以继续划分:

速度从慢到快
虚拟内存-磁盘
物理内存
二级cache
一级cache
寄存器

虚拟内存是一个很伟大的发明,它借助内存管理单元(MMU),并利用分页机制将磁盘的一部分模拟为内存使用。它允许计算机使用硬盘空间来扩展实际的物理内存。这使得操作系统能够运行超过实际物理内存容量的程序。

而我们重点关注的地方在cache这里。

cache命中率

cache会从更低一级的内存结构中搬数据,如果数据访问是局部性很强(如访问同一数据块多次),则缓存命中率会较高,如果不命中,那么计算机会跑到下一级内存中寻找数据,这样程序运行效率就会非常低。

优化

得知了这一点后,我们可以考虑改善我们的程序写法了,以数组操作为例:

for(int i = 0; i<= 2; i++){for(int j = i; j<= 2; j++)Z[j][i] = 0;
}

在C语言中,二维数组的内存分布通常是按行优先(Row-major order)存储的,这意味着数组的行是连续存储在内存中的。具体来说,对于一个二维数组 Z,其内存布局是按以下方式排列的:

二维数组的内存分布

假设我们有一个二维数组 Z,其大小为 mn 列。数组元素在内存中的排列顺序如下:

Z[0][0], Z[0][1], ..., Z[0][n-1], Z[1][0], Z[1][1], ..., Z[1][n-1], ..., Z[m-1][0], ..., Z[m-1][n-1]

每一行的元素是连续存储的,然后依次存储下一行的元素。

那么,优化后的遍历方法如下:

for (int j = 0; j <= 2; j++) {for (int i = 0; i <= j; i++) {Z[j][i] = 0;}
}

上面的优化方法相信大家都能琢磨出来,但是,如果稍微改一下呢?

for(int i = 0; i<= 5; i++){for(int j = i; j<= 7; j++)Z[j][i] = 0;
}

按照原程序的遍历:

┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ 0,0 │ 1,0 │ 2,0 │ 3,0 │ 4,0 │ 5,0 │ 6,0 │ 7,0 │
│     │ 1,1 │ 2,1 │ 3,1 │ 4,1 │ 5,1 │ 6,1 │ 7,1 │
│     │     │ 2,2 │ 3,2 │ 4,2 │ 5,2 │ 6,2 │ 7,2 │
│     │     │     │ 3,3 │ 4,3 │ 5,3 │ 6,3 │ 7,3 │
│     │     │     │     │ 4,4 │ 5,4 │ 6,4 │ 7,4 │
│     │     │     │     │     │ 5,5 │ 6,5 │ 7,5 │
│     │     │     │     │     │     │ 6,6 │ 7,6 │
│     │     │     │     │     │     │     │ 7,7 │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘

更好的遍历方法:

┌─────┬─────┬─────┬─────┬─────┬─────
│ 0,0 │ 										
│ 1,0 │ 1,1 │  									
│ 2,0 │ 2,1 │ 2,2 │  
│ 3,0 │ 3,1 │ 3,2 │ 3,3 │ 
│ 4,0 │ 4,1 │ 4,2 │ 4,3 │ 4,4 │ 
│ 5,0 │ 5,1 │ 5,2 │ 5,3 │ 5,4 │ 5,5 
| 6,0 | 6,1 | 6,2 | 6,3 | 6,4 | 6,5 
| 7,0 | 7,1 | 7,2 | 7,3 | 7,4 | 7,5 
└─────┴─────┴─────┴─────┴─────┴─────

局部性更好的程序如下,此时想要一眼看出来这样写就有点困难了,那我们要怎么推导数组的遍历式呢:

for (int j = 0; j <= 7; j++) {for (int i = 0; i <= (j < 5 ? j : 5); i++) {Z[j][i] = 0;}
}

引入线性代数

我们先看看各种值的范围:

i的范围: i>=0, i<=5
j的范围: j>=i, j<=7

尝试把它们写成线性方程:

1*i + 0*j + 0 >= 0
-1*i + 0*j + 5 >= 0-1*i + j + 0 >= 0
0*i + -1*j + 7 >= 0

矩阵如下:

|  1  0 |   | i |   >=   | 0 |
| -1  0 | * | j |   >=   | -5 |
| -1  1 |           >=   | 0 |
|  0 -1 |           >=   | -7 |

现在我们得到了矩阵,我们可以进一步得到多面体,先回顾一下矩阵与多面体的关系:

线性约束表示多面体

多面体可以通过一组线性不等式来定义,这些不等式可以表示为矩阵和向量的形式。例如,对于一个包含 n个变量的多面体,可以用一个 m×n 的矩阵A和一个m维的向量 b来表示:

Ax <= b

其中,x是变量向量,约束条件定义了多面体的边界。

顶点表示

多面体的顶点可以通过求解线性方程组(通常涉及矩阵的逆或者伪逆)来获得。这些顶点是满足约束条件的解。

矩阵操作多面体

线性变换

通过矩阵乘法,可以对多面体进行线性变换(如旋转、缩放、平移等)。例如,如果矩阵M描述了一个线性变换,那么多面体中的每一个点 x在变换后的新位置可以表示为Mx。

仿射变换

仿射变换是线性变换的推广,包括线性变换和平移。可以用如下形式表示:

y=Mx+t

其中,MM 是线性变换矩阵,t是平移向量。

好吧,其实矩阵和多面体与接下来要讲的算法也没多大关系,笔者只是想说明如何从不等式推导到线性代数并扩展到多面体和高维空间体的。

使用Fourier-Motzkin算法

Fourier-Motzkin算法是一种经常在多面体中用于求解线性不等式系统的消去算法,概括如下:

选择消去变量: 选择一个变量 xi作为消去变量。

分类不等式: 将所有不等式分为三类:

  • 包含 Xi的不等式,且 Xi的系数为正。
  • 包含 Xi的不等式,且 Xi的系数为负。
  • 不包含 Xi的不等式。

生成新不等式: 通过将第一类不等式和第二类不等式配对,消去 Xi

组合不等式: 将生成的新不等式与不包含 xi的不等式组合,得到一个新的线性不等式系统。

重复步骤: 对新的线性不等式系统重复上述步骤,直到所有变量都被消去。

应用该算法,我们重新得到范围:

0<=j, 0 <=5
j<=7

那么i和j的范围如下:

L(i):0
U(i):5,jL(i):0
U(J):7

有了这个范围,我们可以得到:

for (int j = 0; j <= 7; j++) {for (int i = 0; i <= min(5,j); i++) {Z[j][i] = 0;}
}

也就是:

for (int j = 0; j <= 7; j++) {for (int i = 0; i <= (j < 5 ? j : 5); i++) {Z[j][i] = 0;}
}

总结

从程序中的优化出发,由程序存储引出cache,再由cache命中率引出数据局部性的重要性,为了提高数据局部性,必须改变循环遍历方法。为了改变循环遍历方法,由不等式引出线性代数,再由线性代数引出多面体,最后使用算法计算约束,得到具有良好局部性的程序。
其实没啥好总结的,只写了一小段,还没写完开头呢,不过先更到这,该上床睡觉了。

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

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

相关文章

Elasticsearch:确保业务规则与语义搜索无缝协作

作者&#xff1a;来自 Elastic Kathleen DeRusso 利用查询规则与语义搜索和重新排序相结合的强大功能。 更多阅读&#xff1a; Elasticsearch 8.10 中引入查询规则 - query rules Elasticsearch 查询规则现已正式发布 - query rules 你是否知道查询规则&#xff08;query ru…

把riscv32位系统弄懂1:riscv32 CPU指令学习

Riscv手册 首先下载手册&#xff1a;文件下载----中国开放指令生态(RISC-V)联盟 从这个页面下载riscv-spec-v2.1中文版 也可以下载中科大的这本&#xff1a;RISC-V手册 Riscv32指令集包括基础指令集和一些扩展指令集&#xff0c;比如在ESP32C3技术手册中&#xff0c;写到E…

全国消费水平系统|Java|SSM|JSP|

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html 5⃣️数据库可…

达梦数据库-读写分离集群部署

读写分离集群部署 读写分离集群由一个主库以及一个或者多个(最多可以配置 8 个)实时备库组成&#xff0c;基于实时归档实现的高性能数据库集群&#xff0c;不但提供数据保护、容灾等数据守护基本功能&#xff0c;还具有读写操作自动分离、负载均衡等特性。同时可以配置确认监视…

【ALGC】探秘 ALGC—— 卓越数据处理能力的科技瑰宝

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 在大数据时代&#xff0c;如何高效地处理和分析海量数据是一个核心挑战。ALGC&#xff08;Advanced Learning and Generalized Comp…

docker仓库用户认证

保证实验环境纯净删除启动的docker 1.安装建立认证文件的工具包 [rootlocalhost ~]# yum install httpd-tools -y 2.创建目录存放认证文件 [rootlocalhost ~]# mkdir auth [rootlocalhost ~]# htpasswd -Bc auth/.htpasswd lee #-B 强制使用最安全加密方式&#xff0c;默认用m…

(OCPP服务器)SteVe编译搭建全过程

注意&#xff1a;建议使用3.6.0&#xff0c;我升级到3.7.1&#xff0c;并没有多什么新功能&#xff0c;反而电表的实时数据只能看到累计电能了&#xff0c;我回退了就正常&#xff0c;数据库是兼容的&#xff0c;java版本换位java11&#xff0c;其他不变就好 背景&#xff1a;…

【IMU:视觉惯性SLAM系统】

视觉惯性SLAM系统简介 相机&#xff08;单目/双目/RGBD)与IMU结合起来就是视觉惯性&#xff0c;通常以单目/双目IMU为主。 IMU里面有个小芯片可以测量角速度与加速度&#xff0c;可分为6轴(6个自由度)和9轴&#xff08;9个自由度&#xff09;IMU&#xff0c;具体的关于IMU的介…

Linux 基本使用和程序部署

1. Linux 环境搭建 1.1 环境搭建方式 主要有 4 种&#xff1a; 直接安装在物理机上。但是Linux桌面使用起来非常不友好&#xff0c;所以不建议。[不推荐]。使用虚拟机软件&#xff0c;将Linux搭建在虚拟机上。但是由于当前的虚拟机软件(如VMWare之类的)存在一些bug&#xff…

c++------------------函数

函数定义 语法格式 函数定义包括函数头和函数体。函数头包含返回类型、函数名和参数列表。函数体是用花括号{}括起来的代码块&#xff0c;用于实现函数的功能。例如&#xff0c;定义一个计算两个整数之和的函数&#xff1a; int add(int a, int b) {return a b; }这里int是返回…

如何在centos系统上挂载U盘

在CentOS上挂载NTFS格式的U盘,需要执行一系列步骤,包括识别U盘设备、安装必要的软件、创建挂载点,并最终挂载U盘。以下是在CentOS上挂载NTFS格式U盘的详细步骤: 一、准备工作 确认CentOS版本: 确保你的CentOS系统已经安装并正常运行。不同版本的CentOS在命令和工具方面可能…

不同路径

不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff…

C++打造局域网聊天室第十课: 客户端编程及数据发送

文章目录 前言一、补充内容&#xff0c;设置显示框换行二、客户端编程三、封装消息发送函数四、所处的身份状态总结 前言 C打造局域网聊天室第十课&#xff1a; 客户端编程及数据发送 一、补充内容&#xff0c;设置显示框换行 编辑框的显示内容默认是不会换行的&#xff0c;这…

理解神经网络

神经网络是一种模拟人类大脑工作方式的计算模型&#xff0c;是深度学习和机器学习领域的基础。 基本原理 神经网络的基本原理是模拟人脑神经系统的功能&#xff0c;通过多个节点&#xff08;也叫神经元&#xff09;的连接和计算&#xff0c;实现非线性模型的组合和输出。每个…

记Fastjson2的一个报ConcurrentModificationException的bug

错误背景&#xff1a;fastjson2的parseObject方法&#xff0c;在spring webflux项目中被调用&#xff0c;有时会报java.util.ConcurrentModificationException错误。报错处的代码如下图&#xff1a; 改了半天与并发安全相关的代码&#xff0c;还是会报此错误。后来改变思路搜…

智慧仓储可视化视频监控智能监管系统解决方案

一、背景与需求 对于现在很多大型工厂或者物流基地来说&#xff0c;仓库无疑是存放物品的重点场所。仓储存放着大量货物&#xff0c;同时存在大量的辅助设备&#xff0c;需要进行全方位的监管&#xff0c;以避免发生安全事故&#xff0c;造成财产损失。原有的人工巡检方式已无法…

信息安全管理与评估赛题第9套

全国职业院校技能大赛 高等职业教育组 信息安全管理与评估 赛题九 模块一 网络平台搭建与设备安全防护 1 赛项时间 共计180分钟。 2 赛项信息 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 网络平台搭建与设备安全防护 任务1 网络平台搭建 XX:XX- XX:XX 50 任务2…

五分钟学会如何在GitHub上自动化部署个人博客(hugo框架 + stack主题)

上一篇文章&#xff1a; 10分钟学会免费搭建个人博客&#xff08;Hugo框架 stack主题&#xff09; 前言 首先&#xff0c;想要实现这个功能的小伙伴需要完成几个前置条件&#xff1a; 有一个GitHub账号安装了git&#xff0c;并可以通过git推送commit到GitHub上完成第一篇文章…

各种电机原理介绍

1&#xff0c;直流电机 &#xff08;1&#xff09;基本原理 直流电动机由直流电驱动电池或外部电源为其供电。在最简单的直流电动机中&#xff0c;定子为永磁体(即红蓝磁体外壳)&#xff0c;转子是一个电磁体(即线圈)&#xff0c;电流通过碳刷和一个换向器作用于转动的线圈。…

Etcd注册中心基本实现

Etcd入门 什么是Etcd GitHub&#xff1a;https://github.com/etcd-io/etcd Etcd数据结构与特性 键值对格式&#xff0c;类似文件层次结构。 Etcd如何保证数据一致性&#xff1f; 表面来看&#xff0c;Etcd支持事务操作&#xff0c;能够保证数据一致性。 底层来看&#xff0…