LISP-字节数组的快速输出



我正在为LISP中的一种语言制作编译器,总体目标是让编译器从原始语言生成LISP代码。在尝试测量生成的代码的性能时,我发现它在打印字符串方面严重不足。

在原始语言中,字符是字节算术值,因此字符串是字节数组,字节的值对应于其值是字节的ascii码的字符。"可打印"字节数组必须以null结尾。因此,要将字节数组打印为字符串,我必须在打印之前将原始数组的元素映射为字符

(defun writeString (X &AUX (NPOS 0) (i 0))
(declare (type (simple-VECTOR fixnum *) x))
(declare (type fixnum NPOS i))
(SETF NPOS (POSITION 0 X))
(IF (NOT NPOS)
(SETF NPOS (LENGTH X)))
(princ (MAKE-ARRAY NPOS
:INITIAL-CONTENTS (map 'vector
#'code-char
(SUBSEQ X 0 NPOS))
:ELEMENT-TYPE 'base-char)))  

并且它被注入到生成的代码中。

使用time运行一个示例代码,我发现princ部分在执行过程中会导致大量的考虑,这会减慢速度。当在make-array...的位置放置一个静态字符串时,没有减速,也没有考虑,所以我想这就是造成损坏的部分。

在编译时,我已经设置了全速标志,字节值目前在生成的代码中声明为fixnum。

有人能为我指出一种更好的方法来将字节数组打印为字符串,同时避免过度考虑吗?

我可以从一开始就将字节存储为字符,但这会导致语言中将它们视为数字的部分由于需要转换而速度较慢。

代码中的问题

您的代码:

(defun writeString (X &AUX (NPOS 0) (i 0))
(declare (type (simple-VECTOR fixnum *) x))
(declare (type fixnum NPOS i))
(SETF NPOS (POSITION 0 X))
(IF (NOT NPOS)
(SETF NPOS (LENGTH X)))
(princ (MAKE-ARRAY NPOS
:INITIAL-CONTENTS (map 'vector
#'code-char
(SUBSEQ X 0 NPOS))
:ELEMENT-TYPE 'base-char)))

代码中有几个错误:

  • i未使用
  • 第一个类型声明在语法上无效
  • NPOS的声明是错误的。您将其定义为FIGNUM,但它可以是NIL

有很多编程错误:

  • 如果只想输出字符,就不需要分配任何数组
  • 即使您想生成一个数组,也可以只生成一次
  • X不是字符串的好名称

一个简单的解决方案:

(defun writestring (bytestring)
(loop for byte across bytestring
while (plusp byte)
do (write-char (code-char byte))))

类型声明的版本可以是:

(defun writestring (bytestring)
(declare (vector bytestring))
(loop for byte of-type (integer 0 255) across bytestring
while (plusp byte)
do (write-char (code-char byte))))

代替CCD_ 5,也可以使用CCD_。

关于生成矢量:

让我们看看您是如何尝试创建阵列的:

使用make数组创建一个数组,使用另一个数组中的内容。为什么不告诉MAP生成正确的数组?

CL-USER 46 > (map '(vector base-char) #'code-char #(102 111 111 98 97 114))
"foobar"

现在,如果您出于某种原因想要分配阵列:

  • 做一次
  • 将内容映射到生成的数组中。使用map-into。它将以较短的顺序停止

示例:

CL-USER 48 > (let ((bytestring #(102 111 111 98 97 114 0 100 100 100)))
(map-into (make-array (or (position 0 bytestring)
(length bytestring))
:element-type 'base-char)
#'code-char
bytestring))
"foobar"

您可以依赖write-sequence,它有望经过优化以编写字符或字节序列。它还接受一个:end参数,该参数对于定义写入字符串的末尾非常有用。

我怀疑您是否真的需要使用文字向量(始终为simple-vector(,但如果需要,您可能需要更改它们。你可以在阅读时间完成:

(let ((input #.(coerce #(102 111 111 98 97 114 0 100 100 100)
'(vector (mod 256)))))
(write-sequence (map '(vector base-char)
#'code-char
input)
*standard-output*
:end (position 0 input)))

我从来没有使用过这样的东西,但你也可以在字符和字节模式下打开同一个文件,并在必要时切换:

(with-open-file (out-c #P"/tmp/test"
:if-exists :supersede
:direction :output)
(with-open-file (out-8 #P"/tmp/test"
:element-type '(unsigned-byte 8)
:direction :output
:if-exists :append)
(format out-c "Hello [")
(file-position out-8 (file-position out-c))
(write-sequence #(102 111 111 98 97 114) out-8)
(file-position out-c (file-position out-8))
(format out-c "]")))

它在/tmp/test中打印"Hello [foobar]",而且它似乎可以处理多字节字符,但您可能需要对其进行更多测试。

相关内容

  • 没有找到相关文章

最新更新