球拍流比自定义流慢?

Ale*_*lex 6 performance sieve-of-eratosthenes racket

我实现了一个简单但效率不高的橡皮擦筛。一次用于内置球拍流,一次用于自定义流。对我而言,唯一已知的区别是,内置流正在评估待命而不是构建的第一项。在两个实现中评估前1000个素数时,自定义流的运行速度是其10-20倍。有什么解释吗?

(define (integers-starting-from-stream n)
  (stream-cons n (integers-starting-from-stream (+ n 1))))

(define (stream-limit s limit)
    (when (not (= limit 0)) (stream-limit (stream-rest s) (- limit 1))))

(define x (integers-starting-from-stream 2))

(define (divisible? x y)
  (= (remainder x y) 0))

(define (sieve s)
  (stream-cons
   (stream-first s)
   (sieve (stream-filter
           (lambda (x)
             (not (divisible? x (stream-first s))))
           (stream-rest s)))))
(time (stream-limit (sieve x) 1000))
Run Code Online (Sandbox Code Playgroud)

还是我误会了一些会影响性能的东西?

(define-syntax-rule (s-delay exp)
  (?() exp))

(define (s-force delayedObject)
  (delayedObject))

(define empty-s 'S-EMPTY-STREAM)

(define (s-empty? s)
  (eq? s empty-s))

(define (s-first s)
  (car s))

(define (s-rest s)
  (s-force (cdr s)))

(define-syntax-rule (s-cons a b)
  (cons a (s-delay b)))
(define (s-filter p s)
  (cond ((s-empty? s) empty-s)
        ((p (s-first s))
         (s-cons (s-first s)
                 (s-filter p (s-rest s))))
        (else (s-filter p (s-rest s)))))
;;;;;;;;;;;;;;;;;;;;
(define (divisible? x y)
  (= (remainder x y) 0))

(define (integers-starting-from n)
  (s-cons n (integers-starting-from (+ n 1))))

(define (s-limit s limit)
    (when (not (= limit 0)) (s-limit (s-rest s) (- limit 1))))

(define x (integers-starting-from 2))

(define (sieve s)
  (s-cons (s-first s) (sieve (s-filter (lambda(x) (not (divisible? x (s-first s)))) (s-rest s)))))
(time (s-limit (sieve x) 1000))
Run Code Online (Sandbox Code Playgroud)

soe*_*ard 4

这是一个观察结果:

使用该版本integers-starting-from-stream打印生成的数字:

(define (integers-starting-from-stream n)
  (stream-cons n
               (begin
                 (display (~a n " "))
                 (integers-starting-from-stream (+ n 1)))))
Run Code Online (Sandbox Code Playgroud)

同样:

(define (integers-starting-from n)
  (s-cons n
          (begin (display (~a n " "))
                 (integers-starting-from (+ n 1)))))
Run Code Online (Sandbox Code Playgroud)

我们可以测试:

(collect-garbage) (collect-garbage) (collect-garbage)
(time (stream-limit (sieve x) 10))
(collect-garbage) (collect-garbage) (collect-garbage)
(time (s-limit (s-sieve s-x) 10))
Run Code Online (Sandbox Code Playgroud)

我们观察到流版本打印从 2 到 51 的数字,而 s 版本打印从 2 到 30 的数字。

流版本生成的列表几乎是双倍大小。

这是流版本比自定义版本慢的第一个(也是最重要的)原因。

流版本较慢的第二个原因是流版本缓存stream-first. 当元素的计算速度较慢时,缓存通常会更快。

(define (integers-starting-from-stream n)
  (stream-cons (begin (sleep 1) n)
               (integers-starting-from-stream (+ n 1))))
Run Code Online (Sandbox Code Playgroud)

(define (integers-starting-from n)
  (s-cons (begin (sleep 1) n)
          (integers-starting-from (+ n 1))))
Run Code Online (Sandbox Code Playgroud)

然后运行:

(collect-garbage) (collect-garbage) (collect-garbage)
(define x (integers-starting-from-stream 2))
(time (stream-limit x 10))
(time (stream-limit x 10))
(collect-garbage) (collect-garbage) (collect-garbage)
(define s-x (integers-starting-from 2))
(time (s-limit s-x 10))
(time (s-limit s-x 10))
Run Code Online (Sandbox Code Playgroud)