二进制外部数组到 Lisp 数组

问题描述 投票:0回答:1

我正在读取一个二进制数据文件,该文件表示 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 上映射文件?”太相似。 我建议他们重新阅读这个问题并注意:

  1. 我已经使用 mmap 来获取数据
  2. 它询问如何将映射内存读取到 Common Lisp 数组中而不进行复制。
common-lisp
1个回答
0
投票

除非实现直接支持这一点,否则根据我的经验,修改数组的内部结构是错误的。 即使您可以说服系统数组的数据位于您想要的位置,那么 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))))))
© www.soinside.com 2019 - 2024. All rights reserved.