如何在 SBCL/Common Lisp 中序列化和加载对象

How to serialize and load an object in SBCL/Common Lisp

我有一个对象 o,它是 SBCL 中 class X 的一个实例。

我想要一个函数 write-X-object 以这样的方式将 o 序列化到一个文件,当使用 load-X-object 读回该文件时,生成的对象等同于 o。

;; writing the object
(write-X-object o "~/tmp/o.serialized")

;; reading the object, much later, 
;; after sbcl has been exited and restarted

(setq v (read-X-object "~/tmp/o.serialized"))

o 可能有 1 GB 大小(或数百万个较小对象的数组),结构复杂,因此我们的想法是尽可能快地读取和写入。

主要有三种方法

  1. 使用 print/read 的内置工具:你可以在 print-object 上为你的 class 定义一个方法,将它序列化(也许你会让它依赖于一些特殊变量所以你不会将千兆字节打印到 repl)。然后你可以定义一个 reader 宏(对应于你用来打印你的对象的任何语法)然后你可以做 (with-open-file (x "/tmp/foo" :direction :output) (print my-object X)) 来保存你的对象,而要取回它你可以做 (with-open-file (x "/tmp/foo") (read x)。优点是这很简单。缺点是速度很慢,而且 space 效率不高。
  2. 您可以利用评论者建议的第三方序列化库,如 conspack。优点:读写速度相当快。缺点:您不能增量读取大于内存的对象。
  3. 您可以重构您的 class(使用 MOP),以便对象可以以完全相同的格式存储在内存和磁盘上,然后使用 mmap 来 read/write 它。一个示例库是 manardb。该系统还允许存储许多不同的对象。优点:没有 read/write to/from 磁盘的开销。可以自动处理大于主内存的对象(为您处理交换 ram in/out)。缺点:访问对象字段的开销可能很小。请注意,使用这种方法,通常可以通过使用可以通过 ffi 指针正确优化访问的实现和使用专用数据结构(例如,浮点数组比一般数组好得多)来避免大部分缺点

比如定义一个方法 print-object,我猜你可以通过 defmethod 覆盖泛型函数 MAKE-LOAD-FORM

make-load-formmake-load-form-saving-slots 已在 HyperSpec 中定义。

它们 return 两种形式,然后您可以将形式写入带有文本的文件,尽管这可能需要更多 space 磁盘(比较写入特定的二进制规范)。


并且 CLiki 有一个关于序列化的页面。我认为现在可以使用一些第三方软件包。