如何在公共lisp中获取64位整数?
我想用common lisp编写一个位板,所以我需要一个64位整数。如何在common lisp中获取64位整数?还有,有没有什么库可以帮助我在不从头开始编写的情况下完成这项工作 您希望使用位向量,它是任意大小的位数组,而不是类似64位整数的东西。实现将为您处理内部表示。您可以将变量声明为如何在公共lisp中获取64位整数?,lisp,common-lisp,bit,clisp,Lisp,Common Lisp,Bit,Clisp,我想用common lisp编写一个位板,所以我需要一个64位整数。如何在common lisp中获取64位整数?还有,有没有什么库可以帮助我在不从头开始编写的情况下完成这项工作 您希望使用位向量,它是任意大小的位数组,而不是类似64位整数的东西。实现将为您处理内部表示。您可以将变量声明为(有符号字节64)或(无符号字节64): 这取决于您的实现,它是否真的足够聪明,可以在8个连续字节中填充,或者它是否将使用bignum。适当的优化-声明可能会有所帮助 下面是一个(非常简单的)此类类型声明和二进
(有符号字节64)
或(无符号字节64)
:
这取决于您的实现,它是否真的足够聪明,可以在8个连续字节中填充,或者它是否将使用bignum。适当的优化-声明可能会有所帮助
下面是一个(非常简单的)此类类型声明和二进制处理整数的示例:
(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扩展器定义,用于获取整数位的简单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
在portable Common Lisp中,“整数”可以任意大。有一个更有效的整数子集称为“fixnums”。FIXNUM的确切范围取决于实现。但通常不能使用完整的64位(在64位体系结构上),因为大多数常见的Lisp实现都需要类型标记位。对于用户来说,没有太大区别。fixnum是整数的子集,可以添加两个fixnum并得到一个非fixnum整数结果。唯一可以观察到的区别是,使用非固定整数的计算速度较慢,需要更多存储。。。通常,如果要使用整数进行计算,则不需要声明要使用64位进行计算。您只需使用整数和对其进行的常规操作
如果您想要真正的64位大整数(仅用64位表示,不带标记等)并使用这些整数进行计算,您将保留可移植的ANSI CL功能。最好在CLISP邮件列表中询问CLISP是否以及如何支持该功能
文件
- 类型
- 类型
使用位向量/阵列实现8x8位电路板的示例
(从残酷和过早优化的代码开始,只是为了显示
获得紧凑汇编代码的方法):
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
s允许本地
对于
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))
; 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
由于还没有SET-BITBOARD
,使用USE-BITBOARD
的示例如下:
(use-bitboard (make-bitboard))
反汇编USE-BITBOARD
(同样是SBCL,Linux x64)显示
编译器内联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))
; 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包括一个
从更简单、更慢的代码开始,不需要声明
速度,是最好的。只是比较一下代码的大小——我从
使用大量声明编写代码,以在最后生成简单的代码
相比之下,看起来更简单:-)。这里的优点是你
尝试时可以将Common Lisp视为脚本/原型语言
拿出想法,然后从
剖析者建议
汇编代码显然不像加载整个电路板那样紧凑
一个64位寄存器,然后访问单个位。但是如果你
突然决定你想要每平方米超过1比特,这太多了
更改CL代码比更改汇编代码更容易(只需
将数组类型从'(mod 2)
更改为'(mod 16)
,以便
实例)。您使用的是哪种Lisp实现?哪种CL实现?尝试在REPL中评估以下两种形式:(lisp实现类型)
和(lisp实现版本)
。使用CLISP,64位内核上最大的fixnum是(2^48)-1,这意味着每个32位字有六到七位用作标记。实现说明中的即时对象表示讨论证实了这一点:另外,请参见:处理位向量的速度是否与处理64位整数的速度一样快?还有,为什么我不能在lisp中访问64位整数?是因为它取决于实现吗?你能再详细一点吗?我使用的是common lisp 2.48,当我键入(带符号的字节64)时,返回了一个错误(带符号的字节64)
是一个类型说明符,而不是一个函数。它只能在类型说明符合法的情况下使用。我在回答中添加了一个示例。或者,设置位的另一种方法是(setf(ldb(byte 1 pos)int)1)
,如本例所示:(let((x 0))(setf(ldb(byte 1 2)x)1)x)
=>2
。公共Lisp中的类型声明是对编译器的提示。公共Lisp整数总是无界的,但大整数将以低效的格式存储,并带有指针(bignums)。在64位系统上,有效的原始整数(fixNum)通常小于64位,因为有些位用于类型标记。编译器可能会使用类型声明来去除类型标记,并使用完整的64位原始整数。问题是你对如何做这件事有一个先入为主的概念,它应该如何表现。然而,Mirons示例展示了如何操作位板