为何在SBCL中设置大型数组元素会产生Consing?如何规避?
在SBCL中设置大型数组元素时产生Consing的原因及解决方法
在SBCL中设置大型数组元素时会产生Consing,频繁执行该操作会导致堆内存迅速耗尽。即使声明了数组的元素类型(single-float),Consing量仅从1600000字节降至800000字节,未彻底解决问题。
测试代码
(defun test-array () (let ((arr1 (make-array 200000 :element-type 'single-float :initial-element 1.0))) (dotimes (x 200000) (setf (aref arr1 x) 0.0)))) (time (test-array))
测试结果
Evaluation took: 0.000 seconds of real time 0.000119 seconds of total run time (0.000112 user, 0.000007 system) 100.00% CPU 235,760 processor cycles 800,016 bytes consed 0.000140 seconds of total run time (0.000132 user, 0.000008 system)
产生Consing的原因
- 字面量类型不匹配:代码中使用的
0.0是默认的double-float类型,而数组元素类型是single-float。每次执行(setf (aref arr1 x) 0.0)时,SBCL需要将double-float的0.0转换为single-float类型,这个转换过程会产生临时的single-float对象,累积后形成大量Consing。 - 默认优化级别不足:在默认编译设置下,编译器没有提前将
0.0转换为single-float常量并复用,而是在每次循环迭代中重复执行转换操作,进一步放大了Consing量。
避免Consing的方法
1. 使用匹配类型的字面量
直接使用single-float类型的字面量(后缀s0),彻底避免类型转换:
(defun test-array () (let ((arr1 (make-array 200000 :element-type 'single-float :initial-element 1.0s0))) (dotimes (x 200000) (setf (aref arr1 x) 0.0s0)))) (time (test-array))
此时测试会发现Consing量降为0,因为无需再进行类型转换,直接用匹配类型的常量赋值。
2. 启用高级编译器优化
通过声明高优化级别,让编译器自动优化类型转换和循环逻辑:
(defun test-array () (declare (optimize (speed 3) (safety 0))) (let ((arr1 (make-array 200000 :element-type 'single-float :initial-element 1.0))) (dotimes (x 200000) (setf (aref arr1 x) 0.0)))) (time (test-array))
在高优化级别下,编译器会提前将0.0转换为single-float常量,避免循环中的重复转换,从而消除Consing。
3. 使用内置数组填充函数
用SBCL内置的fill函数替代手动循环,该函数内部经过优化,不会产生Consing:
(defun test-array () (let ((arr1 (make-array 200000 :element-type 'single-float :initial-element 1.0s0))) (fill arr1 0.0s0))) (time (test-array))
内容的提问来源于stack exchange,提问作者Orm Finnendahl
相关产品推荐
相关产品推荐

