内存数据集产生的隐性成本-阿里云开发者社区

开发者社区> 技术小能手> 正文

内存数据集产生的隐性成本

简介:
+关注继续查看

当我们要对数据做一些非常规的复杂运算时,通常要将数据装入内存。现在也有不少程序设计语言提供了内存数据集对象及基本的运算方法,可以较方便地实现这类运算。不过,如果对内存数据集的工作原理了解不够,就可能写出低效的代码。

我们看数据集的产生。比如要生成一个100行2列的数据集,第一列x为序号,第二列xx是第一列的平方。

第一种方法,先生成一个空数据集,再一行一行地追加数据进去。


A

B

1

=create(x,xx)


2

for 100

>A1.insert(0,A2,A2*A2)

第二种方法,直接产生相应行数的数据集。


A

B

1

=100.new(~:x,~*~:xx)


这两种方法产生的结果集相同,实质的循环次数和每次循环的计算内容看起来也相同,但执行效率却不一样,后者要比前者更快。

数据集在内存中会保存成一个连续的数组。动态追加的数据集,会导致这个数组长度不断地变长,原先为这个数组分配的空间也要扩大。而内存分配不是一件很简单的事情,已经分配好的空间的后面内存很可能已经被占用而不能再继续扩大,只能重新分配一块更大的空间,然后将原空间内的数据复制过来。寻找空间和复制数据都要占用CPU时间,常常比运算本身的消耗都大。一般情况下,分配内存时会多预留一些空间来应付可能的增长,这样不致于每次追加都导致重新内存分配,但也不可能预留太多而浪费内存,如果追加次数过多,仍然还会有不少时间消耗到内存分配上。而如果事先知道数据集行数一次性创建出来后,则只需要在开始做一次内存分配即可。

动态追加的数据集虽然使用起来更灵活,但性能却赶不上静态数据集,在关注性能时要习惯性地避免。


其实,用这个例子来对比并不很恰当,因为有100.new(~:x,~*~:xx)这样的简单写法时,很少人会采用动态追加的数据集了,这里用这个例子主要是为了突出关键差异。而实际应用中会有不少情况下很难用一句话写出数据集生成,程序员就可能习惯性地采用动态追加的数据集了。

举一个更恰当的例子,我们想生成一个20行2列的Fibonacci数据集,第一列key为行号,即1,2,3,…;第二列value为值。Fibonacci数列的规则是:第1、第2行取值为1,从第3行起,取值为前两行之和。这个运算需要一步步实现,使用动态数据集就是很自然的想法了:


A

B

1

=create(key,value)


2

>a=0

>b=1

3

for 20

>A1.insert(0,A3,b)

4


>b=a+b,a=b-a

不过,使用静态数据集的性能更好,即使计算本身仍然需要一步步实现,但数据集可以一次性产生:


A

B

1

=20.new(0:key,0:value)


2

>a=0

>b=1

3

for A1

>A3.key=#A3,A3.value=b

4


>b=a+b,a=b-a

先生成一个都填满0的数据集(随便别的什么数也可以),然后再用循环把正确的值填进去。实质计算量仍然一样,但避免了动态分配。


除了行方向动态追加外,还有列方向的问题,即在数据集上增加新的列。

列追加比行追加要更为复杂。内存数据集中的一行是一条记录,物理上也是一个数组。因为数据结构很少改变,大多数内存数据集不会在生成这个数组时预留空间,否则内存浪费就太多了(每一行都有)。这时候每次追加列时都会发生前面说的重新分配空间,而且要针对每一行记录进行,再将原记录数据抄过来,这个动作的时间成本经常远远超过追加的那个列本身的计算。

内存数据集一般都有提供追加列的功能,这会带来方便性,但在关注性能时却要慎用。能不用则不用,一定需要时,也是如上所述,最好是一次性把需要追加的列都加上,而不要一遍遍地追加。

比如我们想在第一个例子中的数据集上再追加两个列y=x+xx和yy=y*y。看起来较自然的两步写法:


A

B

3

=A1.derive(x+xx:y)

=A3.derive(y*y:yy)

一次性产生列的写法:


A

B

3

=A1.derive(x+xx:y,0:yy)

>A3.run(yy=y*y)

相比之下,后者的性能要更好。


类似技巧还可以用在从数据库中取出的数据集上。

比如要从数据表T中取出字段month和amount并按month排序,然后追加一列计算amount的累计值。先读出再追加列的写法:


A

B

1

=db.query(“select month,amount from T order by month)


2

=A1.derive(0:acc)

>a=0

3

for A1

>A3.acc=(a=a+A3.amount)

用SQL语句先把列生成好的写法:


A

B

1

=db.query(“select month,amount,0 as acc from T order by month)

>a=0

2

for A1

>A2.acc=(a=a+A2.amount)

对于关注性能的程序员,后一种写法会成为习惯。

原文发布时间为:2018-12-4

本文作者:蒋步星

本文来自云栖社区合作伙伴“ 数据蒋堂”,了解相关信息可以关注“shujujiangtang”微信公众号


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
安卓平板体验Java开发,还能白嫖一年阿里无影云!真香
阿里无影云早有耳闻,前两天看朋友发体验照片,可能是程序员天生爱折腾的特性又发挥作用了,自己也没能忍住,赶快下载体验了一把,没想到“很香”。我体验了浏览器端、Windows 客户端和安卓平板端,下面就来聊聊使用的过程和使用体验。内含一年免费无影云的白嫖方法,千万别错过哦~
31 0
JavaScript、PHP、Golang、Haskell、Elixir,哪个才是最佳编程语言?
  在过去的几年里,我有机会尝试了多种不同的编程语言。   我非常喜欢学习不同的语言、方法和范式。我是一个充满好奇心的人,一直对编程语言十分着迷。每种编程语言都是不同的,在本文中,我们来讨论一下以下五大编程语言的优点以及缺点。
5 0
如果你有拖延症,程序员不如试试这个技巧提升效率?
  要吃掉一头大象,每次吃一口。   ——克雷顿·艾布拉姆斯(Creighton Abrams)   造成拖延的首要原因之一,同时也是造成生产力低下的祸根,就是总是在感慨一个问题:好忙啊,问题好大啊……实际上,你并没有真正试着去解决问题。当我们从任务的全貌来审视任务的时候,它们看起来比真实情况都要大,并且更吓人。   在本文中,我会谈及一个能够帮助你克服拖延的提高生产力的窍门:分解任务。通过将大任务分解为小任务,你会发现自己更有动力去完成它们,也更加稳妥地向着目标前进。
9 0
数据类型-数值和字符串 | 学习笔记
快速学习数据类型-数值和字符串。
5 0
程序员真的是吃青春饭吗?如何面对传说中的 35 岁职业焦虑?
  正走在这条路上的你或许也曾想过这些问题。这一次,力扣邀请到了《高效制胜——程序员面试典型题解》作者吴江(迈克老师),分享他 35 岁跳槽,并拿到了技术负责人 Offer,实现收入增长的故事和面试准备经验。   — 01 —   “35 岁危机”真有那么可怕吗?   在 2018 年快过春节的时候,我们部门突然被通知要开一个会,会上通知我们部门要在明年的这个时候被整体裁掉。我在这家五百强外企已经待了五年,当时虽然有这个预感,但是真的听到正式通知时,不免还是感觉有点震惊。
5 0
程序员为什么要持续学习(升级版)
程序员为什么要持续学习(升级版)
3 0
研发职位到底应该怎么设置?(下)
研发职位到底应该怎么设置?(下)
5 0
学习者的窘境:程序员如何有效学习才能有成就感
学习者的窘境:程序员如何有效学习才能有成就感
7 0
Keras之父写给年轻程序员的33条忠告
  代码是一种交流方式,Keras 之父 Fran?ois Chollet 在本文中为我们总结了在开发过程中、API 设计中及软件职业生涯中应该关注哪些要点。原则是形式化的直觉,比原始模式识别适用于更广泛的情况,Fran?ois Chollet 的这份原则清单将带你领略大师的品味。
9 0
研发职位到底应该怎么设置?(上)
研发职位到底应该怎么设置?(上)
9 0
+关注
技术小能手
云栖运营小编~
7208
文章
9
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载