为什么 MIT-Scheme 写出数据这么慢?
Why is MIT-Scheme so slow in writing out data?
在一个较大的程序中,我正在写出一小组 (10^7) 的数字 (0...9)。在 2.6GHz CPU 上使用 MIT-Scheme 10.1.10 时速度非常慢,大约需要 2 分钟。
可能我做错了什么,比如没有缓冲,但在阅读参考指南后我很困惑。我将一切都减少到最低限度:
(define (write-stuff port)
(define (loop cnt)
(if (> cnt 0)
(begin (display "0" port)
(loop (- cnt 1)))))
(loop 10000000))
(call-with-output-file "tmp.txt" write-stuff)
欢迎任何提示...
[编辑] 为清楚起见:数据条目彼此无关,并存储在二维向量中。它们可以被认为是随机的,所以我不喜欢将它们分组(它是一个接一个或一次全部)。您可以考虑将数据定义为
(define (data width height)
(make-initialized-vector width (lambda (x)
(make-initialized-vector height (lambda (x)
(list-ref (list #[=11=] #) (random 2)))))))
显然,kernel/user-switch 需要很多时间,所以最好将其转换为 1 个字符串,并像 @ceving 建议的那样一次性写出。然后它对我来说足够快了,即使它仍然是 20s 16MB。
(define (data->str data)
(string-append* (vector->list (vector-map vector->string data))))
(define dataset (data 4096 4096))
(call-with-output-file "test.txt" (lambda (p)
(display (data->str dataset) p)))
问题不在于 MIT-Scheme 太慢了。问题是,您过度调用了内核函数 write
。您的程序为每个字符从用户模式切换到内核模式。这需要很多时间。如果您在 Bash 中执行相同操作,则需要更长的时间。
您的方案版本:
(define (write-stuff port)
(define (loop cnt)
(if (> cnt 0)
(begin (display "0" port)
(loop (- cnt 1)))))
(loop 10000000))
(call-with-output-file "mit-scheme-tmp.txt" write-stuff)
(exit)
运行 Scheme 版本的包装器:
#! /bin/bash
mit-scheme --quiet --load mit-scheme-implementation.scm
在我的系统上大约需要 1 分钟:
$ time ./mit-scheme-implementation
real 1m3,981s
user 1m2,558s
sys 0m0,740s
Bash也一样:
#! /bin/bash
: > bash-tmp.txt
n=10000000
while ((n > 0)); do
echo -n 0 >> bash-tmp.txt
n=$((n - 1))
done
需要 2 分钟:
$ time ./bash-implementation
real 2m25,963s
user 1m33,704s
sys 0m50,750s
解决方法是:不执行1000万次内核态切换
只执行一次(或至少少执行 4096 次):
(define (write-stuff port)
(display (make-string 10000000 #[=15=]) port))
(call-with-output-file "mit-scheme-2-tmp.txt" write-stuff)
(exit)
程序只需要 11 秒。
$ time ./mit-scheme-implementation-2
real 0m11,390s
user 0m11,270s
sys 0m0,096s
这就是在 C 库中发明缓冲的原因:
https://www.gnu.org/software/libc/manual/html_node/Stream-Buffering.html#Stream-Buffering
在一个较大的程序中,我正在写出一小组 (10^7) 的数字 (0...9)。在 2.6GHz CPU 上使用 MIT-Scheme 10.1.10 时速度非常慢,大约需要 2 分钟。
可能我做错了什么,比如没有缓冲,但在阅读参考指南后我很困惑。我将一切都减少到最低限度:
(define (write-stuff port)
(define (loop cnt)
(if (> cnt 0)
(begin (display "0" port)
(loop (- cnt 1)))))
(loop 10000000))
(call-with-output-file "tmp.txt" write-stuff)
欢迎任何提示...
[编辑] 为清楚起见:数据条目彼此无关,并存储在二维向量中。它们可以被认为是随机的,所以我不喜欢将它们分组(它是一个接一个或一次全部)。您可以考虑将数据定义为
(define (data width height)
(make-initialized-vector width (lambda (x)
(make-initialized-vector height (lambda (x)
(list-ref (list #[=11=] #) (random 2)))))))
显然,kernel/user-switch 需要很多时间,所以最好将其转换为 1 个字符串,并像 @ceving 建议的那样一次性写出。然后它对我来说足够快了,即使它仍然是 20s 16MB。
(define (data->str data)
(string-append* (vector->list (vector-map vector->string data))))
(define dataset (data 4096 4096))
(call-with-output-file "test.txt" (lambda (p)
(display (data->str dataset) p)))
问题不在于 MIT-Scheme 太慢了。问题是,您过度调用了内核函数 write
。您的程序为每个字符从用户模式切换到内核模式。这需要很多时间。如果您在 Bash 中执行相同操作,则需要更长的时间。
您的方案版本:
(define (write-stuff port)
(define (loop cnt)
(if (> cnt 0)
(begin (display "0" port)
(loop (- cnt 1)))))
(loop 10000000))
(call-with-output-file "mit-scheme-tmp.txt" write-stuff)
(exit)
运行 Scheme 版本的包装器:
#! /bin/bash
mit-scheme --quiet --load mit-scheme-implementation.scm
在我的系统上大约需要 1 分钟:
$ time ./mit-scheme-implementation
real 1m3,981s
user 1m2,558s
sys 0m0,740s
Bash也一样:
#! /bin/bash
: > bash-tmp.txt
n=10000000
while ((n > 0)); do
echo -n 0 >> bash-tmp.txt
n=$((n - 1))
done
需要 2 分钟:
$ time ./bash-implementation
real 2m25,963s
user 1m33,704s
sys 0m50,750s
解决方法是:不执行1000万次内核态切换
只执行一次(或至少少执行 4096 次):
(define (write-stuff port)
(display (make-string 10000000 #[=15=]) port))
(call-with-output-file "mit-scheme-2-tmp.txt" write-stuff)
(exit)
程序只需要 11 秒。
$ time ./mit-scheme-implementation-2
real 0m11,390s
user 0m11,270s
sys 0m0,096s
这就是在 C 库中发明缓冲的原因: https://www.gnu.org/software/libc/manual/html_node/Stream-Buffering.html#Stream-Buffering