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

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

简介:
+关注继续查看

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

我们看数据集的产生。比如要生成一个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”微信公众号


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

相关文章
数据挖掘:降低汽油精制过程中的辛烷值损失模型(一)(上)
数据挖掘:降低汽油精制过程中的辛烷值损失模型(一)(上)
32 0
数据挖掘:降低汽油精制过程中的辛烷值损失模型(二)
数据挖掘:降低汽油精制过程中的辛烷值损失模型(二)
15 0
数据挖掘:降低汽油精制过程中的辛烷值损失模型(一)(下)
数据挖掘:降低汽油精制过程中的辛烷值损失模型(一)(下)
25 0
常见的降维技术比较:能否在不丢失信息的情况下降低数据维度
本文将比较各种降维技术在机器学习任务中对表格数据的有效性
42 0
为什么要停止过度使用置换重要性来寻找特征(下)
为什么要停止过度使用置换重要性来寻找特征
27 0
priori 算法的影响因素分析| 学习笔记
快速学习 priori 算法的影响因素分析。
155 0
降低模型输出风险的三种方法(Valohai)
地球上的每一项业务和行为都包含风险,包括机器学习,或者更广泛地说人工智能也是如此。但是,出于多种原因,您公司的传统风险管理通常不会处理 AI 风险。 首先,算法通常基于专有数据,模型和技术是在特定(业务)问题的领域范围内开发的。其次,算法可能是复杂的、不可预测的并且难以解释。最后,ML 场景相对较新,因此我们也缺乏最佳实践,同时,监管滞后。
32 0
指令流水线影响因素分类
指令流水线影响因素分类
64 0
分布外泛化,「经验风险最小化ERM」真的是最好的算法么?
上海交通大学联合华为诺亚方舟实验室 AI 基础理论团队以及香港科技大学,提出了一种新的面向非独立同分布域泛化问题的评价指标 OoD-Bench,同时对 OoD 领域构建了一个统一的框架。
190 0
+关注
技术小能手
云栖运营小编~
文章
问答
文章排行榜
最热
最新
相关电子书
更多
恒逸石化:用计算消耗取代能源消耗
立即下载
恒逸石化 -用计算消耗取代能源消耗
立即下载
改善弱网络-探索移动互联网下弱网络处理方式
立即下载