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

kdo*_*dog 4 serialization sbcl common-lisp

我有一个对象 o,它是 SBCL 中类 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"))
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*son 5

有三种主要方法可以做到这一点

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