我正在读取一个二进制数据文件,该文件表示 32 和 64 位浮点数的 1、2 和 3 维数组,以及一些整数。 该文件设计为由 C 程序读取,在其中可以声明数组,然后将变量指向 mmap 序列中的各个位置。他们的阅读器不到 20 行代码,因为他们只是沿着指针走。
在我看来,这样的事情对于CL来说应该是可能的。有一些库可以解决相反的问题,以一种避免 GC 的方式将 lisp 向量传递给 C,例如 static-vectors、cffi:with-pointer-to-vector-data、FFA 等。
在本例中,我通过实现的 mmap 函数分配内存,因此 GC 不是问题。 我考虑过使用
ccl::array-data-and-offset
和 sb-c::array-storage-vector
并将它们指向 mmap 文件中的正确偏移量。
有没有人对如何将内存区域映射到 lisp 数组而不进行复制或移位有什么好主意? 性能很重要,因为需要进行大量矩阵乘法。我想要一个便携式解决方案,但目前覆盖 SBCL 和 CCL 就足够了。
编辑:一位“乐于助人”的管理员显然没有仔细阅读这个问题,并将其标记为与“如何在 Linux 上映射文件?”太相似。 我建议他们重新阅读这个问题并注意:
除非实现直接支持这一点,否则根据我的经验,修改数组的内部结构是错误的。 即使您可以说服系统数组的数据位于您想要的位置,那么 GC 是否知道这一点仍然是一个令人兴奋的问题。
相反,几乎可以肯定,自己简单地发明数组会更好。 这听起来很疯狂,但实际上非常简单,并且可以获得相当好的性能。
例如,如果您有一个内存块,您知道它是双精度数的行主二维矩阵(比如说),并且您知道行数(我假设您通过深入了解您拥有的数据来找到它),那么如果您可以说服实现将双精度数获取并存储到该 blob 的适当偏移量中,您就可以编写在所有有趣方面将其视为矩阵的代码。 而且该代码可以很快。
下面是我编写的一些代码,它使用 CFFI 来讨论双浮点矩阵。 在 CFFI 中,您需要的访问器是
mem-aref
。 宏 with-double-float-matrix-accessors
定义了这些东西的本地访问器函数,这是受 MACLISP 启发的一个 hack。 至少在理论上,这些访问器应该很快。
您需要收集才能使用它。
(defpackage :ts
(:use :cl :cffi :org.tfeb.hax.collecting))
(in-package :ts)
(deftype matrix-index ()
`(integer 0 (,array-dimension-limit)))
(deftype matrix-rm-index ()
`(integer 0 (,array-total-size-limit)))
(defparameter *matrix-access-checked* t) ;compile time
(defmacro with-double-float-matrix-accessors ((&rest bindings) &body forms)
;; Bind local accessor functions for double matrices
(multiple-value-bind (accessors initforms
vars row-vars row-forms col-forms size-vars)
(with-collectors (accessor initform var row-var row-form col-form
size-var)
(dolist (b bindings)
(destructuring-bind (a i rf cf) b
(accessor a)
(initform i)
(let ((s (symbol-name a)))
(var (make-symbol s))
(row-var (make-symbol (concatenate 'string s "-ROWS")))
(row-form rf)
(col-form cf)
(size-var (make-symbol (concatenate 'string s "-SIZE")))))))
`(let ,(mapcan (lambda (var initform row-var row-form)
`((,var ,initform)
(,row-var ,row-form)))
vars initforms row-vars row-forms)
(declare (type matrix-index ,@row-vars))
(let ,(mapcar (lambda (sv rv cf)
`(,sv (* ,rv ,cf)))
size-vars row-vars col-forms)
(declare (ignorable ,@size-vars))
(declare (type matrix-rm-index ,@size-vars))
(flet ,(mapcan (lambda (accessor var row-var size-var)
`((,accessor (r c)
(declare (type matrix-index r c))
,@(if *matrix-access-checked*
`((assert (< (* r c) ,size-var)
(r c)
"out of range")
'()))
(mem-aref ,var ':double
(+ (* r ,row-var) c)))
((setf ,accessor) (n r c)
(declare (type double-float n)
(type matrix-index r c))
,@(if *matrix-access-checked*
`((assert (< (* r c) ,size-var)
(r c)
"out of range")
'()))
(setf (mem-aref ,var ':double
(+ (* r ,row-var) c)) n))))
accessors vars row-vars size-vars)
(declare (inline ,@(mapcan (lambda (accessor)
`(,accessor
(setf ,accessor)))
accessors))
(ignorable ,@(mapcan (lambda (accessor)
`((function ,accessor)
(function (setf ,accessor))))
accessors)))
,@forms)))))
这是一个将两个这样的矩阵相乘到一个数组中的函数(可能没有很好地优化):
(defun double-float-matrix-multiply->array (a b s
&optional
(c (make-array
(list s s)
:element-type 'double-float)))
(declare (type matrix-index s)
(type (array double-float (* *)) c))
(with-double-float-matrix-accessors ((a a s s)
(b b s s))
(dotimes (row s c)
(dotimes (col s)
(loop with sum of-type double-float = 0.0d0
for k below s
do (incf sum (* (a row k)
(b k col)))
finally (setf (aref c row col) sum))))))