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

4

我在SBCL中有一个类X的实例对象o。

我想要一个函数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"))

这篇文章涉及到的数据可能达到1GB(或由数百万个小对象组成的数组),结构复杂,因此阅读和写入需要尽可能快。


1
请查看 https://github.com/conspack/cl-conspack。 - PuercoPop
2个回答

6
这可以通过以下三种主要方法来实现:
  1. 使用打印/读取的内置功能:您可以为您的类定义一个在 print-object 上的方法,以将其序列化(也许您会使它依赖于某些特殊变量,以便不会向repl中打印几GB)。然后,您可以定义一个读取宏(对应于您用于打印对象的语法),然后保存您的对象时,您将执行 (with-open-file(x“ / tmp / foo”:direction:输出)(print my-object X)),并要获取它回来,您将进行(with-open-file(x“ / tmp / foo”)(read x)。优点是这很简单。缺点是这很慢且不太节省空间。
  2. 您可以利用第三方序列化库,例如评论者建议的conspack。优点:读取或写入相当快速。缺点:不能逐步读取大于内存的对象。
  3. 您可以重构您的类(使用MOP),以便可以在内存和磁盘上以完全相同的格式存储对象,然后使用mmap来读取/写入。一个例子库是manardb。此系统还允许存储许多不同的对象。优点:无需读/写到/从磁盘的开销。可以自动处理大于主内存的对象(它为您处理了换入/换出ram)。缺点:访问对象字段可能有一些小开销。请注意,使用此方法,通过使用可以正确优化ffi指针访问和使用专用数据结构(例如,与通用数组相比,浮点数数组要好得多),通常可以避免大部分缺点。

谢谢。只是为了明确,我试图避免选项1有几个原因:它慢,空间利用不高,并且编码麻烦。我会研究选项2。 - kdog
虽然有其他缺点,但如果文件被压缩,选项1可以非常节省空间(尽管这可能会很慢)。 - Dan Robertson

1

比如定义一个方法print-object,我猜你可以通过defmethod来重载通用函数MAKE-LOAD-FORM

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

它们返回两个表单,然后你可以用文本将表单写入文件,尽管这可能会占用更多的磁盘空间(与写入特定的二进制规范相比)。


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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接