问题描述
我经常处理一些键不完美的数据,并且需要从其他来源连接数据,我想继续使用哈希对象以提高速度,但是当我使用大量数据时,我可以遇到崩溃(内存限制)。
一个简化的概述是,我有2个不同的键,它们都是唯一的,但对于每条记录都不存在,我们将它们称为键1和键2。
我当前的解决方案虽然不是很优雅(但是可以用),但是它可以执行以下操作:
if _N_ = 1 then do;
declare hash h1(Dataset:"DataSet1");
h1.DefineKey("key1");
h1.DefineData("Value");
h1.DefineDone();
declare hash h2(Dataset:"DataSet1");
h2.DefineKey("key2");
h2.DefineData("Value");
h2.DefineDone();
end;
set DataSet2;
rc = h1.find();
if rc NE 0 then do;
rc = h2.find();
end;
所以我在两个哈希表中具有完全相同的数据集,但是定义了2个不同的键,如果找不到第一个键,那么我尝试查找第二个键。
有没有人知道一种使这种方法更有效/更容易读取/减少内存密集型的方法?
抱歉,如果这似乎是完成任务的一种不好方法,我绝对欢迎批评,让我可以学习!
预先感谢
亚当。
解决方法
我非常支持哈希表查找-它们帮助我在几分钟之内完成了数亿行的大规模连接操作,否则可能要花费数小时。
您的操作方式并不坏。如果您发现自己的内存不足,那么首先要确定的是哈希表实际使用了多少内存。 This article by sasnrd shows exactly how to do this。
一旦您弄清了它的使用量并有一个基准,或者如果它由于内存不足而根本无法运行,则可以尝试一些选项,以查看它们如何改善内存使用率和性能。
1。仅包含您需要的密钥和数据
在加载哈希表时,请排除所有不必要的变量。您可以在加载哈希表之前或期间执行此操作。您可以使用数据集选项来帮助减小表的大小,例如where
,keep
和drop
。
dcl hash h1(dataset: 'mydata(keep=key var1)');
2。减少可变长度
长字符变量占用更多内存。将长度减小到最小所需值将有助于减少内存使用量。使用%squeeze()
宏可在加载之前自动将所有变量减小到所需的最小大小。您可以找到该宏here。
%squeeze(mydata,mydata_smaller)
;
3。调整hashexp选项
hashexp
helps improve performance and reduce hash collisions。 hashexp
的较大值将增加内存使用量,但可能会提高性能。较小的值将减少内存使用量。我建议阅读上面的链接,并查看sasnrd在此文章顶部的链接,以了解它如何影响您的加入。该值的大小应根据表的大小进行适当调整。对于应该使用什么值,没有一个确定的答案,我的建议是您的系统可以处理的。
dcl hash h1(dataset: 'mydata',hashexp:2)
;
4。为您的SAS会话分配更多内存
如果您的哈希表经常用光内存,则memsize
的值可能太低。如今,许多机器都有大量的RAM,即使在设备适中的机器上,SAS也可以很好地处理多个很难完成的SAS会话。增大此值可能会产生巨大的变化,但是您要调整此值作为最后的选择。
默认的memsize
选项为2GB。尝试将其增加到4GB,8GB,16GB等,但是不要过度使用,例如将其设置为0以使用所需的内存。您不希望SAS会话占用机器上的所有内存,如果其他用户也在其中。
暂时将其设置为0可能是一个有用的故障排除工具,它可以查看哈希对象未运行时实际占用的内存量。但是,如果它是您自己的计算机,并且您是唯一使用它的计算机,则可以将其设置为0。
memsize
可以在SAS调用时或在SAS Configuration File中直接调整(在9.4上为sasv9.cfg,在Viya中为SASV9_Option环境变量)。
我有一个非常相似的问题,我的处理方法略有不同。
首先:无论问题如何,都应牢记Stu所说的所有内容。
如果您处在无法真正减小字符变量大小的情况下(请记住,无论数据集大小如何,RAM中的所有数字均为8字节,因此请勿尝试缩小它们) ,您可以通过这种方式进行处理。
- 使用key1作为键,key2作为数据以及您的实际数据构建一个哈希表。确保key1是“更好的”键-填充更充分的键。重命名Key2为其他变量名称,以确保您不会覆盖实际的key2。
- 搜索key1。如果找到key1,那就太好了!继续前进。
- 如果key1丢失,则使用一个击打对象(哈希迭代器)遍历搜索key2的所有记录。
如果经常使用key2,这不是很有效。第3步也可能比使用击打手更好,例如,您可以对这些记录进行键设置或其他操作。在我的特殊情况下,表和查找都缺少key1,因此可以简单地遍历缺少key1的小得多的子集-如果在这种情况下,这是不正确的,并且主表中完全填充了两个键,则这会慢很多。
我要考虑的另一件事是放弃哈希表并使用键集,格式或其他不使用RAM的东西。
或拆分您的数据集:
data haskey1 nokey1;
set yourdata;
if missing(key1) then output nokey1;
else output haskey1;
run;
然后是两个数据步骤,一个是带有key1的哈希值,另一个是带有key2的哈希值,然后将两者组合在一起。
其中哪种效率最高,很大程度上取决于您的数据集大小(主数据集和查找数据)以及key1的缺失。