SBCL:Fixnum 优化

Hen*_*nes 4 optimization sbcl common-lisp

我试图通过使用优化和 fixnums 从一个小的二次求解器中获得更高的速度。这是我的代码:

 1: (defun solve-x (d)
 2:   (declare (optimize (speed 3))
 3:               (type fixnum d))
 4:   (let ((x 1) (y 1))
 5:     (declare (type fixnum x y))
 6:     (loop while (/= (- (* x x) (* d y y)) 1) do
 7:       (if (> (- (* x x) (* d y y)) 1)
 8:         (incf y)
 9:         (incf x)))
10:     (list x y)))
Run Code Online (Sandbox Code Playgroud)

SBCL 编译器似乎无法正确优化第 6 行和第 7 行。我收到很多这样的警告:

forced to do GENERIC-- (cost 10)
      unable to do inline fixnum arithmetic (cost 2) because:
      The first argument is a (INTEGER 1 21267647932558653957237540927630737409), not a FIXNUM.
      The second argument is a (INTEGER
                                -98079714615416886892398913872502479823289163909206900736
                                98079714615416886871131265939943825866051622981576163327), not a FIXNUM.
      The result is a (VALUES
                       (INTEGER
                        -98079714615416886871131265939943825866051622981576163326
                        98079714615416886913666561805061133780526704836837638145)
                       &OPTIONAL), not a (VALUES FIXNUM &REST T).
      unable to do inline (signed-byte 64) arithmetic (cost 5) because:
      The first argument is a (INTEGER 1 21267647932558653957237540927630737409), not a (SIGNED-BYTE
                                                                                         64).
      The second argument is a (INTEGER
                                -98079714615416886892398913872502479823289163909206900736
                                98079714615416886871131265939943825866051622981576163327), not a (SIGNED-BYTE
                                                                                                  64).
      The result is a (VALUES
                       (INTEGER
                        -98079714615416886871131265939943825866051622981576163326
                        98079714615416886913666561805061133780526704836837638145)
                       &OPTIONAL), not a (VALUES (SIGNED-BYTE 64) &REST T).
      etc.
Run Code Online (Sandbox Code Playgroud)

不知道在哪里继续。我已经尝试在乘法、除法和减法周围插入“fixnum”,但它只会变得更糟。

任何想法,如何快速做到这一点?

jki*_*ski 5

如果您确定数字在任何时候都不会溢出,您可以添加(SAFETY 0)优化。还要(THE FIXNUM ...)在计算周围添加以告诉 SBCL 您希望将结果视为固定编号。这三个参数*应拆分为两个单独的调用。

您的代码当前正在(- (* x x) (* d y y))循环中计算两次。您应该将其分配给一个变量。还要注意,由于循环中只有XY变化,因此没有必要再次计算另一部分(我不知道那些计算是什么,所以我只是将它们称为FOO,BARQUUX)。

(defun solve-x (d)
  (declare (optimize (speed 3) (safety 0) (debug 0))
           (type fixnum d))
  (let ((x 1) (y 1))
    (declare (type fixnum x y))
    (loop with foo of-type fixnum = (* x x)
          with bar of-type fixnum = (* (the fixnum (* d y)) y)
          for quux of-type fixnum = (- foo bar)
          while (/= quux 1)
          do (if (> quux 1)
                 (setf y (1+ y)
                       bar (* (the fixnum (* d y)) y))
                 (setf x (1+ x)
                       foo (* x x))))
    (list x y)))
Run Code Online (Sandbox Code Playgroud)

为了避免两次编写公式,您可以使用#n=reader 宏。X并且Y也可以作为&AUX变量移动到参数列表中以摆脱LET和 second DECLARE

(defun solve-x (d &aux (x 1) (y 1))
  (declare (optimize (speed 3) (safety 0) (debug 0))
           (type fixnum d x y))
  (loop with foo of-type fixnum = #1=(* x x)
        with bar of-type fixnum = #2=(* d (the fixnum (* y y)))
        for quux of-type fixnum = (- foo bar)
        while (/= quux 1)
        do (if (> quux 1)
               (setf y (1+ y)
                     bar #2#)
               (setf x (1+ x)
                     foo #1#)))
  (list x y))
Run Code Online (Sandbox Code Playgroud)

由于X并且Y总是增加一,您可以通过增加先前的值来避免一些乘法。

(defun solve-x (d &aux (x 1) (y 1))
  (declare (optimize (speed 3) (safety 0) (debug 0))
           (type fixnum d x y))
  (loop with foo of-type fixnum = 1
        with bar of-type fixnum = d
        for quux of-type fixnum = (- foo bar)
        while (/= quux 1)
        do (if (> quux 1)
               (setf bar (+ bar (the fixnum (* d y)))
                     y (1+ y)
                     bar (+ bar (the fixnum (* d y))))
               (setf foo (+ foo x)
                     x (1+ x)
                     foo (+ foo x))))
  (list x y))
Run Code Online (Sandbox Code Playgroud)