我想在常见的lisp中写一个位板,所以我需要一个64位整数。如何在常见的lisp中获得64位整数?此外,是否有任何库可以帮助我实现这一目标而无需从头开始编写所有内容?
答案 0 :(得分:7)
您可以将变量声明为(signed-byte 64)
或(unsigned-byte 64)
:
CL-USER> (typexpand '(unsigned-byte 64))
(INTEGER 0 18446744073709551615)
T
CL-USER> (typexpand '(signed-byte 64))
(INTEGER -9223372036854775808 9223372036854775807)
T
这取决于你的实现,如果它实际上足够聪明,可以在8个连续的字节中填充它,或者它是否会使用bignum。适当的optimize
- 声明可能会有所帮助。
这是一个(非常简单的)这种类型声明的例子,并处理二进制中的整数:
(let* ((x #b01)
(y #b10)
(z (logior x y)))
(declare ((signed-byte 64) x y z))
(format t "~a~%" (logbitp 1 x))
(format t "~a~%" (logbitp 1 (logior x (ash 1 1))))
(format t "~b~%" z))
Output:
NIL
T
11
这是一个setf-expander定义,用于获取整数位的简单setter,以及相应的getter:
(define-setf-expander logbit (index place &environment env)
(multiple-value-bind (temps vals stores store-form access-form)
(get-setf-expansion place env)
(let ((i (gensym))
(store (gensym))
(stemp (first stores)))
(values `(,i ,@temps)
`(,index ,@vals)
`(,store)
`(let ((,stemp (dpb ,store (byte 1 ,i) ,access-form))
,@(cdr stores))
,store-form
,store)
`(logbit ,i ,access-form)))))
(defun logbit (index integer)
(ldb (byte 1 index) integer))
这些可以这样使用:
(let ((x 1))
(setf (logbit 3 x) 1)
x)
==> 9
(let ((x 9))
(setf (logbit 3 x) 0)
x)
==> 1
(logbit 3 1)
==> 0
(logbit 3 9)
==> 1
答案 1 :(得分:6)
在便携式Common Lisp中,“Integers”和你一样大。有一个更高效的整数子集称为“fixnums”。 fixnums的确切范围是实现依赖的。但它通常不是可以使用的完整64位(在64位架构上),因为大多数Common Lisp实现都需要类型标记位。对于用户而言,没有太大区别。 Fixnums是整数的子集,可以添加两个fixnums并获得not-fixnum整数结果。可观察到的唯一差异是使用非fixnum整数的计算速度较慢,需要更多存储空间,...通常,如果要使用整数进行计算,则不需要声明要使用64位进行计算。你只需使用Integers和那些常用的操作。
如果你想要真正的64位大整数(仅用64位表示,没有标签等)并使用这些整数进行计算,你将保留可移植的ANSI CL功能。如果CLISP支持这一点,最好在CLISP邮件列表中询问。
文档
答案 2 :(得分:5)
用于实现8x8位板的位向量/阵列的示例用法 (从残酷和过早优化的代码开始,只是为了展示一个 获得紧密汇编代码的方法):
(defun make-bitboard ()
(make-array '(8 8) :element-type '(mod 2) :initial-element 0))
MAKE-BITBOARD
将创建一个8x8位板作为位数组。什么时候
使用SBCL,这在内部表示为每个元素1位(所以
你有64位+数组实例开销)。如果你要求
在访问电路板时进行优化,您将获得快速代码。
(declaim (inline get-bitboard))
(defun get-bitboard (bit-board x y)
(declare (optimize speed (safety 0) (debug 0))
(type (simple-array (mod 2) (8 8)) bit-board)
(type fixnum x y))
(aref bit-board x y))
(declaim (notinline get-bitboard))
DECLAIM
是允许本地的
inlining requests
GET-BITBOARD
。
使用GET-BITBOARD
:
(defun use-bitboard (bit-board)
(declare (optimize speed (safety 0) (debug 0))
(type (simple-array (mod 2) (8 8)) bit-board)
(inline get-bitboard))
(let ((sum 0))
(declare (type fixnum sum))
(dotimes (i 8)
(declare (type fixnum i))
(dotimes (j 8)
(declare (type fixnum j))
(incf sum (the (mod 2) (get-bitboard bit-board i j)))))
sum))
由于还没有SET-BITBOARD
,因此使用USE-BITBOARD
的示例是:
(use-bitboard (make-bitboard))
反汇编USE-BITBOARD
(再次,SBCL,Linux x64)显示了
编译器内联GET-BITBOARD
:
; disassembly for USE-BITBOARD
; 030F96A2: 31F6 XOR ESI, ESI ; no-arg-parsing entry point
; 6A4: 31D2 XOR EDX, EDX
; 6A6: EB54 JMP L3
; 6A8: 90 NOP
; 6A9: 90 NOP
; 6AA: 90 NOP
; 6AB: 90 NOP
; 6AC: 90 NOP
; 6AD: 90 NOP
; 6AE: 90 NOP
; 6AF: 90 NOP
; 6B0: L0: 31DB XOR EBX, EBX
; 6B2: EB3E JMP L2
; 6B4: 90 NOP
; 6B5: 90 NOP
; 6B6: 90 NOP
; 6B7: 90 NOP
; 6B8: 90 NOP
; 6B9: 90 NOP
; 6BA: 90 NOP
; 6BB: 90 NOP
; 6BC: 90 NOP
; 6BD: 90 NOP
; 6BE: 90 NOP
; 6BF: 90 NOP
; 6C0: L1: 488D04D500000000 LEA RAX, [RDX*8]
; 6C8: 4801D8 ADD RAX, RBX
; 6CB: 4C8B4711 MOV R8, [RDI+17]
; 6CF: 48D1F8 SAR RAX, 1
; 6D2: 488BC8 MOV RCX, RAX
; 6D5: 48C1E906 SHR RCX, 6
; 6D9: 4D8B44C801 MOV R8, [R8+RCX*8+1]
; 6DE: 488BC8 MOV RCX, RAX
; 6E1: 49D3E8 SHR R8, CL
; 6E4: 4983E001 AND R8, 1
; 6E8: 49D1E0 SHL R8, 1
; 6EB: 4C01C6 ADD RSI, R8
; 6EE: 4883C302 ADD RBX, 2
; 6F2: L2: 4883FB10 CMP RBX, 16
; 6F6: 7CC8 JL L1
; 6F8: 4883C202 ADD RDX, 2
; 6FC: L3: 4883FA10 CMP RDX, 16
; 700: 7CAE JL L0
; 702: 488BD6 MOV RDX, RSI
; 705: 488BE5 MOV RSP, RBP
; 708: F8 CLC
; 709: 5D POP RBP
; 70A: C3 RET
不确定为什么编译器会输入所有NOP
s(为...留出空间)
仪表后来?对齐?)但是如果你看一下代码
结束它非常紧凑(不像手工制作的汇编程序那样紧凑
疗程)。
现在这是过早优化的一个明显例子。正确的方法 从这里开始就是简单地写:
(defun get-bitboard (bit-board x y)
(aref bit-board x y))
(defun use-bitboard (bit-board)
(let ((sum 0))
(dotimes (i 8)
(dotimes (j 8)
(incf sum (get-bitboard bit-board i j))))
sum))
...然后在运行使用该游戏的游戏代码时使用分析器 位板,看看CPU瓶颈在哪里。 SBCL包括一个不错的 statistical profiler
从更简单和更慢的代码开始,没有声明 速度,是最好的。只需比较代码的大小 - 我开始使用 代码有很多声明,最后可以编写简单的代码 通过比较看起来更简单:-)。这里的优点是你 在尝试时可以将Common Lisp视为脚本/原型语言 提出想法,然后从代码中挤出更多性能 分析师建议。
汇编代码显然没有加载整个电路板那么紧凑
一个64位寄存器然后访问各个位。但如果你
突然决定你想要每平方超过1位,这很多
更改CL代码比更改汇编代码更容易(只是
将数组类型从'(mod 2)
更改为'(mod 16)
,用于
实例)。
答案 3 :(得分:2)
你想使用位向量,它是任意大小的位数组,而不是64位整数。实施将处理您的内部表示。