SBCL:Fixnum 优化
SBCL: Fixnum Optimizations
我正在尝试通过使用优化和定数来提高二次求解器的速度。这是我的代码:
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)))
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.
不知道,从哪里继续。我已经尝试在乘法、除法和减法周围插入 'the fixnum',但情况只会变得更糟。
有什么想法,如何让它更快?
问题是 fixnum
不是一个很有用的类型。特别是,如果 a
和 b
是 fixnum
,那么 (* a b)
很可能不是:考虑 (* most-positive-fixnum most-positive-fixnum)
:这不是 fixnum
。
所以您需要做的是声明参数具有良好的类型:特别是比 fixnum
足够小的类型,以至于算术不会溢出到 bignum
s。假设您使用的是 64 位平台,这相当容易。
如果您确定数字在任何时候都不会溢出,您可以添加 (SAFETY 0)
进行优化。还要在计算周围添加 (THE FIXNUM ...)
以告诉 SBCL 您希望将结果视为固定数字。三个参数 *
应拆分为两个单独的调用。
您的代码目前正在循环中计算 (- (* x x) (* d y y))
两次。您应该将其分配给一个变量。另请注意,由于循环中只有 X
或 Y
发生变化,因此无需再次计算另一部分(我不知道这些计算是什么,所以我将它们称为 FOO
, BAR
和 QUUX
)。
(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)))
为避免必须编写两次公式,您可以使用 #n=
reader 宏。 X
和 Y
也可以作为 &AUX
变量移动到参数列表中,以摆脱 LET
和第二个 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))
由于 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))
我不知道这些数字在您的应用程序中可以达到多大,但是通过将它们声明为(有符号字节 31)可以使速度再提高 25%。
(deftype int31 (&optional (bits 31)) `(signed-byte ,bits))
(defun solve-x (d &aux (x 1) (y 1))
(declare (optimize (speed 3) (safety 0) (debug 0))
(type int31 d x y))
(loop with foo of-type int31 = 1
with bar of-type int31 = d
for quux of-type int31 = (- foo bar)
while (/= quux 1)
do (if (> quux 1)
(setf bar (+ bar (the int31 (* d y)))
y (1+ y)
bar (+ bar (the int31 (* d y))))
(setf foo (+ foo x)
x (1+ x)
foo (+ foo x))))
(list x y))
我正在尝试通过使用优化和定数来提高二次求解器的速度。这是我的代码:
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)))
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.
不知道,从哪里继续。我已经尝试在乘法、除法和减法周围插入 'the fixnum',但情况只会变得更糟。
有什么想法,如何让它更快?
问题是 fixnum
不是一个很有用的类型。特别是,如果 a
和 b
是 fixnum
,那么 (* a b)
很可能不是:考虑 (* most-positive-fixnum most-positive-fixnum)
:这不是 fixnum
。
所以您需要做的是声明参数具有良好的类型:特别是比 fixnum
足够小的类型,以至于算术不会溢出到 bignum
s。假设您使用的是 64 位平台,这相当容易。
如果您确定数字在任何时候都不会溢出,您可以添加 (SAFETY 0)
进行优化。还要在计算周围添加 (THE FIXNUM ...)
以告诉 SBCL 您希望将结果视为固定数字。三个参数 *
应拆分为两个单独的调用。
您的代码目前正在循环中计算 (- (* x x) (* d y y))
两次。您应该将其分配给一个变量。另请注意,由于循环中只有 X
或 Y
发生变化,因此无需再次计算另一部分(我不知道这些计算是什么,所以我将它们称为 FOO
, BAR
和 QUUX
)。
(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)))
为避免必须编写两次公式,您可以使用 #n=
reader 宏。 X
和 Y
也可以作为 &AUX
变量移动到参数列表中,以摆脱 LET
和第二个 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))
由于 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))
我不知道这些数字在您的应用程序中可以达到多大,但是通过将它们声明为(有符号字节 31)可以使速度再提高 25%。
(deftype int31 (&optional (bits 31)) `(signed-byte ,bits))
(defun solve-x (d &aux (x 1) (y 1))
(declare (optimize (speed 3) (safety 0) (debug 0))
(type int31 d x y))
(loop with foo of-type int31 = 1
with bar of-type int31 = d
for quux of-type int31 = (- foo bar)
while (/= quux 1)
do (if (> quux 1)
(setf bar (+ bar (the int31 (* d y)))
y (1+ y)
bar (+ bar (the int31 (* d y))))
(setf foo (+ foo x)
x (1+ x)
foo (+ foo x))))
(list x y))