swa*_*ils 1 sbcl common-lisp clos memory-efficient
语境:
我已经启动了一个 CL 长期项目,其中一个子组件是惰性编程框架,旨在尽可能与外部 CL 代码兼容。
其中一门课是lazy-cons.
(defclass lazy-cons (thunk sequences:sequence)
((head :initform nil :initarg :head :accessor :head)
(tail :initform nil :initarg :tail :accessor :tail))) ;; slot 'gen is defined in the thunk superclass, as well
Run Code Online (Sandbox Code Playgroud)
然而,当尝试对超过 100 万个数字的惰性列表进行排序时,SBCL 不断耗尽堆空间并崩溃。
尝试的方法:
我已经实现了另一个类,可以通过缓存向量减少这个问题。
但我仍然想让lazy-cons自己更加节省空间,因为这个项目旨在成为其他计算项目的后台框架,其中一些项目需要高性能计算。即使最高性能的代码最终传递给 CFFI,Lisp 端仍然应该尽可能高效和可扩展。
对于这个项目,我还需要能够扩展类并用于defmethod专门化某些功能(例如,这样我就可以使用该trivial-extensible-sequences库)。这排除了使用structures较小的表示,因为structure元类不允许从普通类继承。
问题:
是否有可能(例如通过 MOP?)更有效地控制对象的内部表示,同时仍然允许类继承和方法专门化?
如果没有,是否有某种方法至少可以防止 Lisp 实现耗尽堆空间?
(我知道你可以增加分配的堆,但这仍然不能保证你不会意外地陷入LDB;理想情况下,当堆被分配时,代码应该陷入一种情况(可以自动解决)即将失败,而不仅仅是崩溃。)
摘要:您可能无能为力,但至少您的问题似乎是其他问题。
\n在 64 位平台上(嗯,特别是在 ARM64 上,尽管我认为 x64 是相同的),这里是各种对象类型的经验分配(这些是通过分配一个大数组并查看需要多少空间,然后分配一个大数组来实现的)数组并用各种对象的许多副本填充它,看看占用了多少空间,然后进行适当的算术,并检查这一切是否有意义):
\nstandard-class每对槽 48 字节 + 16 字节;structure-class16 字节 + 每对插槽 16 字节;t\xe2\x80\x93 的数组 16 个字节 + 每 2 个元素 16 个字节;single-float\xe2\x80\x93 的数组 16 字节 + 每 4 个元素 16 字节。“每m元素n字节”的出现是因为分配发生在双字(16 字节)边界上。
\n从一个或多个超类继承插槽的类的实例不需要更多的空间,并且没有更大的标头。
\n这表明,如果您需要在槽中保留非直接对象(或大型直接对象,如 fixnums 所说),那么槽的标准 CLOS 表示确实是尽可能好的。
\n假设您确实想要这样做,我相当确定您会这样做,那么您获胜的唯一方法是节省标头开销,对于默认 CLOS 对象来说,这实际上是额外的四个字(32 字节)。我的猜测是,这是一次无望的努力:那个头的存在是有原因的,除了大手术之外,你不可能让它消失。
\n但您遇到的问题非常令人惊讶:standard-class在最合理的情况(具有两个字标头的数组)上,一百万个成熟的实例的开销为 32MB。百万元素列表的总大小为 16MB,作为具有两个槽的结构实现的等效事物的总大小为 32MB,或者作为具有两个槽的 CLOS 对象的总大小为 64MB。因此,与列表相比,您所支付的成本是 48MB(毫不奇怪:48 字节标头的一百万个副本),而最合理的成本是 16MB:好 32MB。
我没有做任何事情来设置 SBCL 上的堆大小,它从 1GB 开始。这意味着一百万个 48 位标头的开销是可用堆空间的 4.8%:如果达到此限制,您几乎已经耗尽内存了。
\n所以我的直觉是这里还有其他问题。如果不是排序算法,那么如果这些东西代表惰性对象,那么你很可能会使用函数来以某种方式代表本质上的承诺,而正是这些东西杀死了你。我想不出任何在标准 CL 中实现惰性的方法,但它不能做到这一点:如果是这样的话,开销是不可避免的。但即便如此,功能总体来说也不是那么庞大。
\n据我所知,您至少可以让 SBCL 来处理存储耗尽错误:以下对我有用:
\n(defun foo (n)\n (handler-case\n (length (make-array n))\n (storage-condition (c)\n c)))\nRun Code Online (Sandbox Code Playgroud)\n但是我不知道这有多可靠:特别是我怀疑有时甚至没有足够的空间来收集垃圾。
\n