serialization: Micro-optimize string literal output in 'write-file-tree'.

This reduces allocations and bit twiddling in the loop.

* guix/serialization.scm (write-literal-strings): New macro.
(write-file-tree): Use it in lieu of 'write-string' calls where applicable.
This commit is contained in:
Ludovic Courtès 2021-02-25 15:46:22 +01:00
parent 9ce78f27a0
commit f293705d7e
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5

View file

@ -199,6 +199,37 @@ (define (read-store-path p)
(define write-store-path-list write-string-list) (define write-store-path-list write-string-list)
(define read-store-path-list read-string-list) (define read-store-path-list read-string-list)
(define-syntax write-literal-strings
(lambda (s)
"Write the given literal strings to PORT in an optimized fashion, without
any run-time allocations or computations."
(define (padding len)
(let ((m (modulo len 8)))
(if (zero? m)
0
(- 8 m))))
(syntax-case s ()
((_ port strings ...)
(let* ((bytes (map string->utf8 (syntax->datum #'(strings ...))))
(len (fold (lambda (bv size)
(+ size 8 (bytevector-length bv)
(padding (bytevector-length bv))))
0
bytes))
(bv (make-bytevector len))
(zeros (make-bytevector 8 0)))
(fold (lambda (str offset)
(let ((len (bytevector-length str)))
(bytevector-u32-set! bv offset len (endianness little))
(bytevector-copy! str 0 bv (+ 8 offset) len)
(bytevector-copy! zeros 0 bv (+ 8 offset len)
(padding len))
(+ offset 8 len (padding len))))
0
bytes)
#`(put-bytevector port #,bv))))))
(define-condition-type &nar-read-error &nar-error (define-condition-type &nar-read-error &nar-error
nar-read-error? nar-read-error?
@ -332,14 +363,12 @@ (define p port)
(define-values (type size) (define-values (type size)
(file-type+size f)) (file-type+size f))
(write-string "(" p) (write-literal-strings p "(")
(case type (case type
((regular executable) ((regular executable)
(write-string "type" p) (write-literal-strings p "type" "regular")
(write-string "regular" p)
(when (eq? 'executable type) (when (eq? 'executable type)
(write-string "executable" p) (write-literal-strings p "executable" ""))
(write-string "" p))
(let ((input (file-port f))) (let ((input (file-port f)))
(dynamic-wind (dynamic-wind
(const #t) (const #t)
@ -348,28 +377,23 @@ (define-values (type size)
(lambda () (lambda ()
(close-port input))))) (close-port input)))))
((directory) ((directory)
(write-string "type" p) (write-literal-strings p "type" "directory")
(write-string "directory" p)
(let ((entries (postprocess-entries (directory-entries f)))) (let ((entries (postprocess-entries (directory-entries f))))
(for-each (lambda (e) (for-each (lambda (e)
(let* ((f (string-append f "/" e))) (let* ((f (string-append f "/" e)))
(write-string "entry" p) (write-literal-strings p "entry" "(" "name")
(write-string "(" p)
(write-string "name" p)
(write-string e p) (write-string e p)
(write-string "node" p) (write-literal-strings p "node")
(dump f) (dump f)
(write-string ")" p))) (write-literal-strings p ")")))
entries))) entries)))
((symlink) ((symlink)
(write-string "type" p) (write-literal-strings p "type" "symlink" "target")
(write-string "symlink" p)
(write-string "target" p)
(write-string (symlink-target f) p)) (write-string (symlink-target f) p))
(else (else
(raise (condition (&message (message "unsupported file type")) (raise (condition (&message (message "unsupported file type"))
(&nar-error (file f) (port port)))))) (&nar-error (file f) (port port))))))
(write-string ")" p))) (write-literal-strings p ")")))
(define port-conversion-strategy (define port-conversion-strategy
(fluid->parameter %default-port-conversion-strategy)) (fluid->parameter %default-port-conversion-strategy))