在共同的lisp中是否存在“元组”等价的东西?

时间:2013-10-20 06:35:05

标签: tuples common-lisp

在我的项目中,我需要处理很多坐标,在2D情况下,我发现(cons x y)的构造比(list x y)(vector x y)快。

但是,我不知道如何将cons扩展到3D或更远,因为我找不到像cons3这样的内容。在common-lisp中有快速tuple的解决方案吗?

为了说明,我做了以下测试:

* (time (loop repeat 10000 do (loop repeat 10000 collect (cons (random 10) (random 10)))))

Evaluation took:
  7.729 seconds of real time
  7.576000 seconds of total run time (7.564000 user, 0.012000 system)
  [ Run times consist of 0.068 seconds GC time, and 7.508 seconds non-GC time. ]
  98.02% CPU
  22,671,859,477 processor cycles
  3,200,156,768 bytes consed

NIL
* (time (loop repeat 10000 do (loop repeat 10000 collect (list (random 10) (random 10)))))

Evaluation took:
  8.308 seconds of real time
  8.096000 seconds of total run time (8.048000 user, 0.048000 system)
  [ Run times consist of 0.212 seconds GC time, and 7.884 seconds non-GC time. ]
  97.45% CPU
  24,372,206,280 processor cycles
  4,800,161,712 bytes consed

NIL
* (time (loop repeat 10000 do (loop repeat 10000 collect (vector (random 10) (random 10)))))

Evaluation took:
  8.460 seconds of real time
  8.172000 seconds of total run time (8.096000 user, 0.076000 system)
  [ Run times consist of 0.260 seconds GC time, and 7.912 seconds non-GC time. ]
  96.60% CPU
  24,815,721,033 processor cycles
  4,800,156,944 bytes consed

NIL

3 个答案:

答案 0 :(得分:3)

这种数据结构的一般方法是使用defstruct。这是您在Common Lisp中创建数据结构的方法。所以,如果你想在三维空间中有一个点,这或多或少都是你要做的:

(defstruct point-3d x y z)

为什么这比阵列更好:

  1. 正确命名。

  2. 它创建了一堆你正在创建的有用的东西,比如访问器,测试某些数据是否属于这种类型的函数,构造这种类型的对象的函数以及其他一些好东西。

  3. 然后在数组中打字更精细:您可以分别指定每个插槽的类型。

  4. 专业打印功能,可以很好地打印您的数据。

  5. 为什么这比列表更好:

    1. 您可以通过执行以下操作来始终要求结构表现为列表:

    2. (defstruct (point-3d (:type list)) x y z)
      
      • 所有与数组相同的东西。

      优化问题:

      您应该尝试探索其他替代方案。创建数组或等效内存印记的缺点单元之间的区别不值得优化。如果您遇到此特定操作的问题,则应该认为该任务通常无法管理。但我真的认为应首先尝试对象池,memoization和一般缓存等技术。

      另一个要点:您没有告诉编译器尝试生成有效的代码。您可以告诉编译器优化大小,速度或调试。在指定要尝试提取的优化类型后,您应该真正衡量性能。


      我写了一个快速测试,看看有什么区别:

      (defstruct point-3d
        (x 0 :type fixnum)
        (y 0 :type fixnum)
        (z 0 :type fixnum))
      
      (defun test-struct ()
        (declare (optimize speed))
        (loop :repeat 1000000 :do
           (make-point-3d :x (random 10) :y (random 10) :y (random 10))))
      
      (time (test-struct))
      
      ;; Evaluation took:
      ;;   0.061 seconds of real time
      ;;   0.060000 seconds of total run time (0.060000 user, 0.000000 system)
      ;;   98.36% CPU
      ;;   133,042,429 processor cycles
      ;;   47,988,448 bytes consed
      
      (defun test-array ()
        (declare (optimize speed))
        (loop :repeat 1000000
           :for point :of-type (simple-array fixnum (3)) :=
           (make-array 3 :element-type 'fixnum) :do
           (setf (aref point 0) (random 10)
                 (aref point 1) (random 10)
                 (aref point 2) (random 10))))
      
      (time (test-array))
      
      ;; Evaluation took:
      ;;   0.048 seconds of real time
      ;;   0.047000 seconds of total run time (0.046000 user, 0.001000 system)
      ;;   97.92% CPU
      ;;   104,386,166 processor cycles
      ;;   48,018,992 bytes consed
      

      我的测试的第一个版本出现了偏差,因为我在第一次测试之前忘了运行GC,因此在上一次测试之后不得不回收内存而使其处于不利地位。现在数字更精确,并且还表明使用结构和数组几乎没有区别。

      所以,再次,按照我之前的建议:使用对象池,memoization,或者你可能想到的任何其他优化技术。优化这里是一个死胡同。

答案 1 :(得分:2)

使用声明和内联函数,结构可能比数组和列表都快:

(declaim (optimize (speed 3) (safety 0) (space 3)))

(print "Testing lists");
(terpri)

(time (loop repeat 10000 do
       (loop repeat 10000
        collect (list (random 1000.0)
                      (random 1000.0)
                      (random 1000.0)))))

(print "Testing arrays");
(terpri)

(declaim (inline make-pnt))
(defun make-pnt (&rest coords)
  (make-array 3 :element-type 'single-float :initial-contents coords))

(time (loop repeat 10000 do
       (loop repeat 10000
        collect (make-pnt (random 1000.0)
                          (random 1000.0)
                          (random 1000.0)))))

(print "Testing structs")
(terpri)

(declaim (inline new-point))
(defstruct (point 
         (:type (vector single-float))
         (:constructor new-point (x y z)))
  (x 0.0 :type single-float)
  (y 0.0 :type single-float)
  (z 0.0 :type single-float))

(time (loop repeat 10000 do 
       (loop repeat 10000
          collect (new-point (random 1000.0)
                             (random 1000.0)
                             (random 1000.0)))))


"Testing lists" 
Evaluation took:
  8.940 seconds of real time
  8.924558 seconds of total run time (8.588537 user, 0.336021 system)
  [ Run times consist of 1.109 seconds GC time, and 7.816 seconds non-GC time. ]
  99.83% CPU
  23,841,394,328 processor cycles
  6,400,180,640 bytes consed


"Testing arrays" 
Evaluation took:
  8.154 seconds of real time
  8.140509 seconds of total run time (7.948497 user, 0.192012 system)
  [ Run times consist of 0.724 seconds GC time, and 7.417 seconds non-GC time. ]
  99.84% CPU
  21,743,874,280 processor cycles
  4,800,178,240 bytes consed


"Testing structs" 
Evaluation took:
  7.631 seconds of real time
  7.620476 seconds of total run time (7.432464 user, 0.188012 system)
  [ Run times consist of 0.820 seconds GC time, and 6.801 seconds non-GC time. ]
  99.86% CPU
  20,350,103,048 processor cycles
  4,800,179,360 bytes consed

答案 2 :(得分:1)

我假设你正在使用浮点值,在这种情况下(make-array 3 :element-type 'single-float)可能是最好的。这样,您可以期望浮动存储为未装箱(在大多数实现中)。

请务必随身携带(declare (type (simple-array single-float (3))))