Skip to content

LISP - 专用数组

现代 Lisp:用于提升性能的专业化数组

Section titled “现代 Lisp:用于提升性能的专业化数组”

在 Common Lisp 中,标准数组是一种通用容器,能够存储任何类型的对象。尽管它非常灵活,但这种泛型性会带来性能开销。Lisp 系统在每次访问元素时都必须动态检查其类型。

**专业化数组(Specialized arrays)**是一种强大的优化特性。它们被限制为只存储单一、特定类型的元素。这种约束允许 Lisp 实现更高效地存储数据并更快地执行操作。

  • 类型安全(Type Safety): Lisp 系统强制执行类型约束。尝试存储错误类型的元素将引发 TYPE-ERROR 错误,从而及早捕获错误。
  • 效率提升(Increased Efficiency): 通过在编译时知晓每个元素的类型,Lisp 可以为数组访问生成高度优化的机器代码,消除运行时类型检查的开销。
  • 优化内存使用(Optimized Memory Usage): 专业化数组,特别是对于数值类型,以紧凑的连续内存块存储,类似于 C 或 Fortran 中的数组。这减少了内存占用并改善了缓存局部性。

你可以使用 make-array 函数并传入 :element-type 关键字参数来创建一个专业化数组。提供的值是一个类型说明符。

以下是一些常用的元素类型:

;; 位数组(0和1),非常适合布尔标志或位掩码。
(make-array 10 :element-type 'bit)
;; 定长整数数组。'(unsigned-byte 8)' 在八位字节流中非常常见。
(make-array 10 :element-type '(unsigned-byte 8))
;; 字符数组,也是字符串的底层类型。
(make-array 10 :element-type 'character)
;; 单精度浮点数数组。
(make-array 10 :element-type 'single-float)
;; 双精度浮点数数组,用于更高精度。
(make-array 10 :element-type 'double-float)

让我们创建一个专业化数组来存储整数,对其进行初始化,并观察其行为。

(defparameter *my-int-array*
(make-array 5
:element-type 'integer
:initial-element 0)
"一个用于存储整数的专业化数组。")
;; 用一些值填充数组
(setf (aref *my-int-array* 0) 100)
(setf (aref *my-int-array* 1) -50)
(setf (aref *my-int-array* 2) 42)
;; 打印数组以查看其内容
(print *my-int-array*)
;; 下面这行代码会引发 TYPE-ERROR,因为我们不能将字符串
;; 存储在专门用于整数的数组中。
;; (setf (aref *my-int-array* 3) "hello")

执行代码会产生以下结果:

#(100 -50 42 0 0)
  • 何时使用: 在对性能敏感的代码中使用专业化数组,例如数值算法、图像处理,或者与二进制数据流或外部库(如通过 CFFI)交互时。
  • 何时不使用: 对于通用数据存储,当灵活性比原始速度更重要时,标准的通用数组完全可以,并且通常更简单易用。
  • 延伸阅读: Common Lisp Hyperspec (CLHS) 是权威资料。查阅 make-array 和“类型说明符(Type Specifiers)”一节以查看所有可能的元素类型。