Racket 球拍中的/列表与/字节的自定义

Racket 球拍中的/列表与/字节的自定义,racket,list-comprehension,for-comprehension,Racket,List Comprehension,For Comprehension,我在玩球拍,错过了一个字节字符串理解。当我在文档中找到带有示例的for/fold/derived时,我决定使用自己的字节字符串理解宏,就像任何初学者一样: (define-syntax (for/bytes stx) (syntax-case stx () ((_ clauses . defs+exprs) (with-syntax ((original stx)) #'(let-values (((bstr i ma

我在玩球拍,错过了一个字节字符串理解。当我在文档中找到带有示例的for/fold/derived时,我决定使用自己的字节字符串理解宏,就像任何初学者一样:

(define-syntax (for/bytes stx)
    (syntax-case stx ()
      ((_ clauses . defs+exprs)
       (with-syntax ((original stx))
         #'(let-values
             (((bstr i max-length)
               (for/fold/derived original ((bstr (make-bytes 16)) (c 0) (ln-incr 32)) clauses
                 (define el (let () . defs+exprs))
                 (let-values (((new-bstr new-ln-incr)
                           (if (eq? c (bytes-length bstr))
                       (values (bytes-append bstr (make-bytes ln-incr)) (* ln-incr 2))
                       (values bstr ln-incr))))
                   (bytes-set! new-bstr c el)
               (values new-bstr (+ c 1) new-ln-incr)))))
     (subbytes bstr 0 i))))))    
我有几个相关的问题:

这是球拍的路线吗? 宏可以吗?基本上,我将for/fold/derived文档中的示例与for/vector中的宏相结合 是否有任何明显的性能优化? 遗憾的是,它并不比/list的list->bytes快。。。这一微观基准:

(define size 50000)
(define (custom-byte-test) (for/bytes ((i (in-range size))) (modulo i 256)))
(define (standard-list-test) (list->bytes (for/list ((i (in-range size))) (modulo i 256))))
(profile-thunk custom-byte-test #:repeat 1000)
(profile-thunk standard-list-test #:repeat 1000)
给出3212ms与3690ms的对比。对于比50000 my For/bytes小得多的大小,它将丢失,对于比50000 my For/bytes大得多的大小,它将赢得。

我的答案:

这是球拍的路线吗

宏可以吗?基本上,我将for/fold/derived文档中的示例与宏expand for/vector结合起来

是的,我觉得它看起来不错

是否有任何明显的性能优化?遗憾的是,它并不比/list的list->bytes快

我不知道怎样做得更快。这里的好处是,对for/bytes的用户隐藏了缓冲区大小调整的复杂性。

我的答案:

这是球拍的路线吗

宏可以吗?基本上,我将for/fold/derived文档中的示例与宏expand for/vector结合起来

是的,我觉得它看起来不错

是否有任何明显的性能优化?遗憾的是,它并不比/list的list->bytes快


我不知道怎样做得更快。这里的好处是,对于for/bytes的用户来说,缓冲区大小调整的复杂性是隐藏的。

我将代码的速度提高了一点

1由于您已经知道当前长度,因此不需要在内部循环中计算字节长度。我用代表当前长度和增加长度的bstr长度替换了您的ln增量

这就提高了约15%

2由于您已经进行了长度检查,因此可以安全地使用不安全的字节集!,这又使速度提高了约10%

在我的机器上,自定义字节测试现在是~1200ms,而标准列表测试是~1750ms


我把你的代码加快了一点

1由于您已经知道当前长度,因此不需要在内部循环中计算字节长度。我用代表当前长度和增加长度的bstr长度替换了您的ln增量

这就提高了约15%

2由于您已经进行了长度检查,因此可以安全地使用不安全的字节集!,这又使速度提高了约10%

在我的机器上,自定义字节测试现在是~1200ms,而标准列表测试是~1750ms


有趣的通常,我不会像在C/C++中那样费心缓存{string,bytes}长度,因为在Racket中,我假定它不超过一个应用程序加上一个struct de-ref。然而,即使这是真的,在这种情况下,它在内部循环中所占的比例也足够高。进步不错,很有趣。通常,我不会像在C/C++中那样费心缓存{string,bytes}长度,因为在Racket中,我假定它不超过一个应用程序加上一个struct de-ref。然而,即使这是真的,在这种情况下,它在内部循环中所占的比例也足够高。很好的改进。
#lang racket
(require racket/unsafe/ops profile)

(define-syntax (for/bytes stx)
  (syntax-case stx ()
    [(_ clauses . defs+exprs)
     (with-syntax ([original stx])
       #'(let ([init-bstr-len 32])
           (let-values
             ([(bstr i max-length)
               (for/fold/derived 
                original 
                ([bstr (make-bytes init-bstr-len)]
                 [c 0]
                 [bstr-len init-bstr-len]) ; <-- use as curr len + extend len
                clauses
                (define el (let () . defs+exprs))
                (let-values 
                  ([(new-bstr new-bstr-len)
                    (if (= c bstr-len) ; <-- remove len calculation
                        (values 
                          (bytes-append bstr (make-bytes bstr-len))
                          (* bstr-len 2))
                        (values bstr bstr-len))])
                  (unsafe-bytes-set! new-bstr c el) ; <-- unsafe op
                  (values new-bstr (add1 c) new-bstr-len)))])
             (subbytes bstr 0 i))))]))

(define size 50000)
(define (custom-byte-test) 
  (for/bytes ([i (in-range size)]) (modulo i 256)))
(define (standard-list-test) 
  (list->bytes (for/list ([i (in-range size)]) (modulo i 256))))
(profile-thunk custom-byte-test #:repeat 1000)
(profile-thunk standard-list-test #:repeat 1000)