我听说SAS将字符变量存储在8字节的块中。
因此,我们的想法是,我们应该总是将字符变量的长度赋值为8的倍数。
我已经搜索过了,找不到任何支持最初断言的证据。
是真的吗?这在文档中有涉及吗?
对于不包含8字节数值变量的数据集是正确的。我将单独发布的数据集。
不,8字节字符可变长度没有什么特别的。
见下文:
data length8;
length char0001-char9999 $8;
call missing(of _all_);
do _i = 1 to 100;
output;
end;
drop _i;
run;
data length7;
length char0001-char9999 $7;
call missing(of _all_);
do _i = 1 to 100;
output;
end;
drop _i;
run;
data length4;
length char0001-char9999 $4;
call missing(of _all_);
do _i = 1 to 100;
output;
end;
drop _i;
run;
data length12;
length char0001-char9999 $12;
call missing(of _all_);
do _i = 1 to 100;
output;
end;
drop _i;
run;
data length16;
length char0001-char9999 $16;
call missing(of _all_);
do _i = 1 to 100;
output;
end;
drop _i;
run;
data length17;
length char0001-char9999 $17;
call missing(of _all_);
do _i = 1 to 100;
output;
end;
drop _i;
run;
这些数据集的大小不同,大致与字符变量的长度成正比。请注意,4的大小按比例要大一些(无论如何,在我的机器上):实际上,4、5、6都是相同的大小。这是因为页面大小的原因:在我的安装中,最小的页面大小是64kb(65535字节),而4、5、6都只能容纳一行数据(大约40、50和60kb行)。这并不是因为为字符变量保存了任何特定的大小,而是因为数据记录的总长度。
这就是您可以通过更改少量来节省的地方:如果您的数据碰巧被安排为页面大小刚好是行大小的两倍,那么使行稍微小一点将节省一半的空间。这种情况不太可能发生,除非在极少数情况下-它需要非常宽的行(许多变量,或非常长的字符变量)。不过,您也可以使用选项更改页面大小,这可能是处理此类边缘情况的更好方法。
对于包含数字变量的数据集,如@jaamor的示例所包含的,存在与8字节大小相关的存储有一些影响的差异。它通常不会对数据集大小产生重大影响,除非在非常高和窄的数据集上,但对于 非常高和窄的数据集,这可能是一个考虑因素。
当一个长度为8字节(默认值)的数字变量时,SAS将这些数字变量放在数据向量的末尾,并以8字节的倍数开始,可能是为了帮助访问这些可预测的数字变量的效率。除8字节数字之外的任何其他变量将被放置在数据向量的开头,然后添加任何填充以使其达到8字节的倍数,然后将8字节数字变量放置在其后。
这可以通过查看一些示例数据集的proc contents
输出来看出。
data fourteen_eight;
length x y $7; *14 total;
length i 8;
run;
data twelve_eight;
length x y $6; *12 total;
length i 8;
run;
data twelve_six;
length x y $6; *12 total;
length i 6;
run;
data twelve_six_eight;
length x y $6;
length z 6;
length i 8;
run;
fourteen_eight
的概念观测长度为22,但物理观测长度为24(以PROC CONTENTS
为例)。twelve_eight
的概念长度为20,但物理观测长度也为24。twelve_six
的概念长度为18,物理观察长度为18——这意味着如果数值变量不是8长,则没有缓冲区。twelve_six_eight
的概念长度为26,物理大小为32:18四舍五入为24,最后是8。(您可以通过简单地添加几个6字节的数字来验证它不是为每个数字变量分配8;它们从不增加总内边距,并且能很好地适应较小的空间。
结果如下:
- <
- x 6美元/gh><
- y 6美元/gh>z 6
- 我8
应该是这样的:
[00000000011111111112222222222333333333344444444445]
[12345678901234567890123456789012345678901234567890]
[xxxxxxyyyyyyzzzzzz iiiiiiii]
旁注:我不能100%确定它不是[iiiiiixxxxxxyyyyyyzzzz]。这就像预测数值变量的位置一样有效。但是,这并不会真正影响到这一点:无论哪种方式,如果您的非8字节数字存储总量不是8字节的倍数,如果您确实有一个或多个8字节数字变量,则会有一个小缓冲区。
正如Joe所说,我使用以下脚本进行了经验测试:
libname testlen "<directory>";
%macro create_ds(length=, dsName=);
data &dsName;
length x $&length.;
do i=1 to 1000000;
x="";
output;
end;
run;
%mend;
%macro create_all_ds;
%do i=1 %to 20;
%create_ds(length=&i, dsName=testlen.len&i)
%end;
%mend;
%create_all_ds
所有数据集都有一个变量。变量的长度因数据集而异,从1到20不等。
数据集1-8占用约15.8 MB
数据集9-16占用~23.7 MB
数据集16-20占用约31.5 MB
这可能意味着对于1个变量数据集,声明不是8 的倍数的SAS变量长度是不节省空间的。
我对2个变量数据集:
进行了类似的测试 %macro create_ds(length=, dsName=);
data &dsName;
length x y $&length.;
do i=1 to 1000000;
x="";
y="";
output;
end;
run;
%mend;
%macro create_all_ds;
%do i=1 %to 20;
%create_ds(length=&i, dsName=testlen.len&i)
%end;
%mend;
%create_all_ds
结果如下:
数据集1-4占用约15.8 MB
数据集5-8占用约23.7 MB
这可能意味着对于有效的长度声明,字符变量长度的和应该是8的倍数。