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

简介:

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

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


相关文章
|
2月前
|
数据采集 人工智能 测试技术
3倍生成速度还降内存成本,超越Medusa2的高效解码框架终于来了
【5月更文挑战第21天】CLLM,一种新方法,通过并行解码提升大型语言模型推理速度3-4倍,降低内存成本,超越Medusa2。采用Jacobi解码和微调策略,保证生成质量。无需修改模型架构,训练成本低,可与现有技术集成。但依赖高质量数据集,更大数据集可提高泛化能力。[链接](https://arxiv.org/pdf/2403.00835)
41 2
|
11月前
|
机器学习/深度学习 算法
减少内存消耗、降低大模型训练成本,ACL杰出论文作者揭秘CAME优化器
减少内存消耗、降低大模型训练成本,ACL杰出论文作者揭秘CAME优化器
177 0
|
存储 弹性计算 运维
雪球引入阿里云云原生内存数据库Tair,实现成本降低50%
在满足雪球行情业务高并发、低时延需求的同时,还解决了成本高昂、扩缩容不便、运维困难等问题。
371 0
雪球引入阿里云云原生内存数据库Tair,实现成本降低50%
|
机器学习/深度学习 存储 人工智能
英特尔推出两款 7nm AI 芯片,内存高达 96GB HBM2e,训练成本更低
当地时间 5 月 10 日,英特尔举办了 2022 英特尔 On 产业创新峰会。在此次峰会上,英特尔公布了其在芯片、软件和服务方面取得的多项进展,并宣布了包括第 12 代英特尔酷睿 HX 处理器家族、阿波罗计划、Greco AI 加速芯片在内的一系列重大发布。
245 0
英特尔推出两款 7nm AI 芯片,内存高达 96GB HBM2e,训练成本更低
|
存储 消息中间件 机器学习/深度学习
硬件成本降低90%,性能提升20倍,异构内存show给你看!
硬件成本降低90%,性能提升20倍,异构内存show给你看!
383 0
硬件成本降低90%,性能提升20倍,异构内存show给你看!
|
存储 弹性计算 NoSQL
突破内存应用瓶颈,让IT成本下降40%的秘诀
近两年5G、大数据、云计算一直为行业热点,数字化进程不断加速,全行业数据开始爆发式增长。面对数据的迅猛增长,企业一方面享受着数据化转型带来的红利,另一方面也承担着大内存运行实例的高额开支。传统内存面临挑战,持久内存方案开始受到了行业更多的关注。
突破内存应用瓶颈,让IT成本下降40%的秘诀
|
6天前
|
存储 分布式计算 Hadoop
HadoopCPU、内存、存储限制
【7月更文挑战第13天】
33 14
|
10天前
|
存储 Java 程序员
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
35 10
|
27天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
28 2