时间复杂度是衡量算法执行效率的一种标准。但是,时间复杂度并不能跟性能划等号。在真实的软件开发中,即便在不降低时间复杂度的情况下,也可以通过一些优化手段,提升代码的执行效率。毕竟,对于实际的软件开发来说,即便是像 10%、20% 这样微小的性能提升,也是非常可观的。

算法的目的就是为了提高代码执行的效率。那当算法无法再继续优化的情况下,我们该如何来进一步提高执行效率呢?我们今天就讲一种非常简单但又非常好用的优化方法,那就是并行计算。今天,我就通过几个例子,给你展示一下,如何借助并行计算的处理思想对算法进行改造?

并行排序

假设我们要给大小为 8GB 的数据进行排序,并且,我们机器的内存可以一次性容纳这么多数据。对于排序来说,最常用的就是时间复杂度为 O(nlogn) 的三种排序算法,归并排序、快速排序、堆排序。从理论上讲,这个排序问题,已经很难再从算法层面优化了。而利用并行的处理思想,我们可以很轻松地将这个给 8GB 数据排序问题的执行效率提高很多倍。具体的实现思路有下面两种。

第一种是对归并排序并行化处理。我们可以将这 8GB 的数据划分成 16 个小的数据集合,每个集合包含 500MB 的数据。我们用 16 个线程,并行地对这 16 个 500MB 的数据集合进行排序。这 16 个小集合分别排序完成之后,我们再将这 16 个有序集合合并。

第二种是对快速排序并行化处理。我们通过扫描一遍数据,找到数据所处的范围区间。我们把这个区间从小到大划分成 16 个小区间。我们将 8GB 的数据划分到对应的区间中。针对这 16 个小区间的数据,我们启动 16 个线程,并行地进行排序。等到 16 个线程都执行结束之后,得到的数据就是有序数据了。

对比这两种处理思路,它们利用的都是分治的思想,对数据进行分片,然后并行处理。它们的区别在于,第一种处理思路是,先随意地对数据分片,排序之后再合并。第二种处理思路是,先对数据按照大小划分区间,然后再排序,排完序就不需要再处理了。这个跟归并和快排的区别如出一辙。

这里我还要多说几句,如果要排序的数据规模不是 8GB,而是 1TB,那问题的重点就不是算法的执行效率了,而是数据的读取效率。因为 1TB 的数据肯定是存在硬盘中,无法一次性读取到内存中,这样在排序的过程中,就会有频繁地磁盘数据的读取和写入。如何减少磁盘的 IO 操作,减少磁盘数据读取和写入的总量,就变成了优化的重点。不过这个不是我们这节要讨论的重点,你可以自己思考下

并行查找

我们知道,散列表是一种非常适合快速查找的数据结构

如果我们是给动态数据构建索引,在数据不断加入的时候,散列表的装载因子就会越来越大。为了保证散列表性能不下降,我们就需要对散列表进行动态扩容。对如此大的散列表进行动态扩容,一方面比较耗时,另一方面比较消耗内存。比如,我们给一个 2GB 大小的散列表进行扩容,扩展到原来的 1.5 倍,也就是 3GB 大小。这个时候,实际存储在散列表中的数据只有不到 2GB,所以内存的利用率只有 60%,有 1GB 的内存是空闲的。

实际上,我们可以将数据随机分割成 k 份(比如 16 份),每份中的数据只有原来的 1/k,然后我们针对这 k 个小数据集合分别构建散列表。这样,散列表的维护成本就变低了。当某个小散列表的装载因子过大的时候,我们可以单独对这个散列表进行扩容,而其他散列表不需要进行扩容

还是刚才那个例子,假设现在有 2GB 的数据,我们放到 16 个散列表中,每个散列表中的数据大约是 150MB。当某个散列表需要扩容的时候,我们只需要额外增加 150*0.5=75MB 的内存(假设还是扩容到原来的 1.5 倍)。无论从扩容的执行效率还是内存的利用率上,这种多个小散列表的处理方法,都要比大散列表高效

当我们要查找某个数据的时候,我们只需要通过 16 个线程,并行地在这 16 个散列表中查找数据。这样的查找性能,比起一个大散列表的做法,也并不会下降,反倒有可能提高。

当往散列表中添加数据的时候,我们可以选择将这个新数据放入装载因子最小的那个散列表中,这样也有助于减少散列冲突。

并行字符串匹配

我们前面学过,在文本中查找某个关键词这样一个功能,可以通过字符串匹配算法来实现。我们之前学过的字符串匹配算法有 KMP、BM、RK、BF 等。当在一个不是很长的文本中查找关键词的时候,这些字符串匹配算法中的任何一个,都可以表现得非常高效。但是,如果我们处理的是超级大的文本,那处理的时间可能就会变得很长,那有没有办法加快匹配速度呢?

我们可以把大的文本,分割成 k 个小文本。假设 k 是 16,我们就启动 16 个线程,并行地在这 16 个小文本中查找关键词,这样整个查找的性能就提高了 16 倍。16 倍效率的提升,从理论的角度来说并不多。但是,对于真实的软件开发来说,这显然是一个非常可观的优化。

不过,这里还有一个细节要处理,那就是原本包含在大文本中的关键词,被一分为二,分割到两个小文本中,这就会导致尽管大文本中包含这个关键词,但在这 16 个小文本中查找不到它。实际上,这个问题也不难解决,我们只需要针对这种特殊情况,做一些特殊处理就可以了。

我们假设关键词的长度是 m。我们在每个小文本的结尾和开始各取 m 个字符串。前一个小文本的末尾 m 个字符和后一个小文本的开头 m 个字符,组成一个长度是 2m 的字符串。我们再拿关键词,在这个长度为 2m 的字符串中再重新查找一遍,就可以补上刚才的漏洞了。

并行搜索

前面我们学习过好几种搜索算法,它们分别是广度优先搜索、深度优先搜索、Dijkstra 最短路径算法、A* 启发式搜索算法。对于广度优先搜索算法,我们也可以将其改造成并行算法。

广度优先搜索是一种逐层搜索的搜索策略。基于当前这一层顶点,我们可以启动多个线程,并行地搜索下一层的顶点。在代码实现方面,原来广度优先搜索的代码实现,是通过一个队列来记录已经遍历到但还没有扩展的顶点。现在,经过改造之后的并行广度优先搜索算法,我们需要利用两个队列来完成扩展顶点的工作。

假设这两个队列分别是队列 A 和队列 B。多线程并行处理队列 A 中的顶点,并将扩展得到的顶点存储在队列 B 中。等队列 A 中的顶点都扩展完成之后,队列 A 被清空,我们再并行地扩展队列 B 中的顶点,并将扩展出来的顶点存储在队列 A。这样两个队列循环使用,就可以实现并行广度优先搜索算法。

总结

当要处理的数据规模达到一定程度之后,我们无法通过继续优化算法,来提高执行效率 的时候,我们就需要在实现的思路上做文章,利用更多的硬件资源,来加快执行的效率。所以,在很多超大规模数据处理中,并行处理的思想,应用非常广泛,比如 MapReduce 实际上就是一种并行计算框架

思考

假设我们有 n 个任务,为了提高执行的效率,我们希望能并行执行任务,但是各个任务之间又有一定的依赖关系,如何根据依赖关系找出可以并行执行的任务?

参考spark dag

引用

51 | 并行算法:如何利用并行处理提高算法的执行效率?

并行算法:如何利用并行处理提高算法的执行效率?相关推荐

  1. 算法高级(36)-如何利用并行提高算法的执行效率?

    前面我们学习了那么多的算法,大家也知道,算法的目的是为了解决实际问题的.但我们也看到,对于同一个问题,我们可以用不同的算法来解决.而因为时间和空间复杂度的问题,不同的算法执行的效率差距还是比较大的.我 ...

  2. 30 个提高Web 程序执行效率的好经验[转]

    1. 尽量避免使用DOM.当需要反复使用DOM 时,先把对DOM 的引用存到JavaScript 本地变量里再使用.使用设置innerHTML的方法来替换document.createElement/ ...

  3. 【数据结构与算法-java实现】一 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?

    今天开始学习程序的灵魂:数据结构与算法. 本文是自己学习极客时间专栏-数据结构与算法之美后的笔记总结.如有侵权请联系我删除文章. 我们都知道,数据结构和算法本身解决的是"快"和&q ...

  4. 解读30个提高Web程序执行效率的好经验

    其实微博是个好东西,关注一些技术博主之后,你不用再逛好多论坛了,因为一些很好的文章微博会告诉你,最近看到酷勤网推荐的一篇文章<30个提高Web程序执行效率的好经验>,文章写得不错,提到一些 ...

  5. 利用JS提高组合拼接字符串效率的方法

    日志原文: http://uniso.blog.sohu.com/104853107.html 利用JS提高组合拼接字符串效率的方法 希望跟各位高手交流,搞过web开发的朋友都知道,在脚本开发过程中, ...

  6. 03|复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?

    目录 为什么需要复杂度分析? 大 O 复杂度表示法 时间复杂度分析 几种常见时间复杂度 空间复杂度分析 为什么需要复杂度分析? 事后统计法:代码跑一遍,通过统计.监控,就能得到算法执行的时间和占用的内 ...

  7. 将m文件通过matlab 的编译器转换为可执行文件,【转】 Matlab中提高m文件执行效率的小技巧...

    在使用数组或矩阵之前先定义维数 MATLAB中的变量在使用之前不需要明确地定义和指定维数.但当未预定义数组或矩阵的维数时,当需赋值的元素下标超出现有的维数时,MATLAB 就为该数组或矩阵扩维一次,这 ...

  8. 提高java代码执行效率的技巧

    对于细节问题我一直认识不怎么深刻,改变我这个看法的是很多年前玩dnf的时候,辛辛苦苦刷来一身的史诗装备,最后却发现打出的伤害并没有想象中那么令人满意,甚至令人感到有点沮丧.后来舍友告诉我,你要注意细节 ...

  9. 提高python执行效率_关于提高python程序执行效率的思路和想法

    原博文 2016-06-09 22:25 − 相比编译型语言(C系列)python胜在简介的语法和优雅的动态编程体验,但是在执行效率上,python有解释性语言先天的劣势--执行效率较低,为了让编写出 ...

最新文章

  1. Smarty中文手册,Smarty教程,Smarty模板的入门教材
  2. 洛谷 P1343 地震逃生
  3. |NOIOJ|动态规划|3532:最大上升子序列和
  4. K-means的缺点(优化不仅仅是最小化误差)
  5. input输入框小写字母自动转换成大写字母
  6. 升级步骤linux_开发人员福音,在win10系统上安装linux子系统
  7. 基于python的图书管理系统测试步骤_Django admin实现图书管理系统菜鸟级教程完整实例...
  8. AngularJS-demo - 常用命令、内置服务、自定义服务、继承
  9. C语言2020年作业,2020年哈尔滨工业大学C语言程序设计精髓 第七周编程题作业
  10. 虚拟机危险!一个存在11年的缓冲区溢出漏洞--毒液
  11. CUDA ---- GPU架构(Fermi、Kepler)
  12. Mac 2016 运行Emacs,M-x键失效问题
  13. Comsol学习——经典案例:水净化反应器
  14. 计算机教程打字方法,技巧:打字指法和关键位置教程_IT /计算机_信息
  15. pdflib 下载地址
  16. Facebook工程师告诉你,如何正确的阅读《算法导论》(CLRS)?
  17. 付出不亚于任何人的努力
  18. 战胜人类最强大脑只是开始,百度大脑“代言”中国人工智能
  19. Casbin明日之星预选生计划-Talent for Casbin 2021(长期招聘)
  20. MYSQL数据库⾯试题

热门文章

  1. educoder MongoDB 数据库基本操作
  2. matlab 矩阵除法计算,Matlab中的矩阵除法有问题???
  3. nginx实现网站url带参跳转 POST请求GET请求跳转
  4. linux 脚本启动oracle,linux自动启动 oracle脚本
  5. php金额,PHP 如何处理金额
  6. chrome charset使用_SourceMap-使用教程
  7. c语言 链表_小陈的C语言笔记---链表(详细讲解基本操作和概念)
  8. oracle中subject是什么意思,subject是什么意思_subject的翻译_音标_读音_用法_例句_爱词霸在线词典...
  9. 3Y叔的clusterProfiler-book阅读Chapter 3 Universal enrichment analysis
  10. channelinboundhandler中都包含了哪一类的方法_标准气体的分类类别与相关气体配置方式方法...