grafts: Improve performance for derivations with many inputs.

Partly fixes <https://bugs.gnu.org/41702>.
Reported by Lars-Dominik Braun <ldb@leibniz-psychology.org>.

Previously we'd potentially traverse the same sub-graph of DEPS several
times.

With this patch, command:

  guix environment --ad-hoc r-learnr --search-paths

goes from 11.3s to 4.6s.

* guix/grafts.scm (reference-origin): Rename to...
(reference-origins): ... this.  Change 'item' parameter to 'items'.
[lookup-derivers]: New procedure.
(cumulative-grafts)[dependency-grafts]: Change 'item' to 'items' and use
'reference-origins'.
Remove 'mapm' around 'dependency-grafts' call.
This commit is contained in:
Ludovic Courtès 2020-06-06 18:46:49 +02:00
parent 22fdca91a9
commit 58bb833365
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5

View file

@ -20,10 +20,12 @@ (define-module (guix grafts)
#:use-module (guix store) #:use-module (guix store)
#:use-module (guix monads) #:use-module (guix monads)
#:use-module (guix records) #:use-module (guix records)
#:use-module (guix combinators)
#:use-module (guix derivations) #:use-module (guix derivations)
#:use-module ((guix utils) #:select (%current-system)) #:use-module ((guix utils) #:select (%current-system))
#:use-module (guix sets) #:use-module (guix sets)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-9 gnu) #:use-module (srfi srfi-9 gnu)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
#:use-module (srfi srfi-34) #:use-module (srfi srfi-34)
@ -183,32 +185,47 @@ (define-syntax-rule (with-cache key exp ...)
(set-current-state (vhash-cons key result cache)) (set-current-state (vhash-cons key result cache))
(return result))))))) (return result)))))))
(define (reference-origin drv item) (define (reference-origins drv items)
"Return the derivation/output pair among the inputs of DRV, recursively, "Return the derivation/output pairs among the inputs of DRV, recursively,
that produces ITEM. Return #f if ITEM is not produced by a derivation (i.e., that produce ITEMS. Elements of ITEMS not produced by a derivation (i.e.,
it's a content-addressed \"source\"), or if it's not produced by a dependency it's a content-addressed \"source\"), or not produced by a dependency of DRV,
of DRV." have no corresponding element in the resulting list."
(define (lookup-derivers drv result items)
;; Return RESULT augmented by all the drv/output pairs producing one of
;; ITEMS, and ITEMS stripped of matching items.
(fold2 (match-lambda*
(((output . file) result items)
(if (member file items)
(values (alist-cons drv output result)
(delete file items))
(values result items))))
result items
(derivation->output-paths drv)))
;; Perform a breadth-first traversal of the dependency graph of DRV in ;; Perform a breadth-first traversal of the dependency graph of DRV in
;; search of the derivation that produces ITEM. ;; search of the derivations that produce ITEMS.
(let loop ((drv (list drv)) (let loop ((drv (list drv))
(items items)
(result '())
(visited (setq))) (visited (setq)))
(match drv (match drv
(() (()
#f) result)
((drv . rest) ((drv . rest)
(if (set-contains? visited drv) (cond ((null? items)
(loop rest visited) result)
(let ((inputs (derivation-inputs drv))) ((set-contains? visited drv)
(or (any (lambda (input) (loop rest items result visited))
(let ((drv (derivation-input-derivation input))) (else
(any (match-lambda (let*-values (((inputs)
((output . file) (map derivation-input-derivation
(and (string=? file item) (derivation-inputs drv)))
(cons drv output)))) ((result items)
(derivation->output-paths drv)))) (fold2 lookup-derivers
inputs) result items inputs)))
(loop (append rest (map derivation-input-derivation inputs)) (loop (append rest inputs)
(set-insert drv visited))))))))) items result
(set-insert drv visited)))))))))
(define* (cumulative-grafts store drv grafts (define* (cumulative-grafts store drv grafts
#:key #:key
@ -233,25 +250,27 @@ (define (graft-origin? drv graft)
(_ (_
#f))) #f)))
(define (dependency-grafts item) (define (dependency-grafts items)
(match (reference-origin drv item) (mapm %store-monad
((drv . output) (lambda (drv+output)
;; If GRAFTS already contains a graft from DRV, do not override it. (match drv+output
(if (find (cut graft-origin? drv <>) grafts) ((drv . output)
(state-return grafts) ;; If GRAFTS already contains a graft from DRV, do not
(cumulative-grafts store drv grafts ;; override it.
#:outputs (list output) (if (find (cut graft-origin? drv <>) grafts)
#:guile guile (state-return grafts)
#:system system))) (cumulative-grafts store drv grafts
(#f #:outputs (list output)
(state-return grafts)))) #:guile guile
#:system system)))))
(reference-origins drv items)))
(with-cache (cons (derivation-file-name drv) outputs) (with-cache (cons (derivation-file-name drv) outputs)
(match (non-self-references store drv outputs) (match (non-self-references store drv outputs)
(() ;no dependencies (() ;no dependencies
(return grafts)) (return grafts))
(deps ;one or more dependencies (deps ;one or more dependencies
(mlet %state-monad ((grafts (mapm %state-monad dependency-grafts deps))) (mlet %state-monad ((grafts (dependency-grafts deps)))
(let ((grafts (delete-duplicates (concatenate grafts) equal?))) (let ((grafts (delete-duplicates (concatenate grafts) equal?)))
(match (filter (lambda (graft) (match (filter (lambda (graft)
(member (graft-origin-file-name graft) deps)) (member (graft-origin-file-name graft) deps))