From mboxrd@z Thu Jan 1 00:00:00 1970 From: taylanbayirli@gmail.com (Taylan Ulrich =?utf-8?Q?Bay=C4=B1rl=C4=B1?= =?utf-8?Q?=2FKammer?=) Subject: [PATCH] build: pull: Compile .scm files in one process. Date: Thu, 05 Nov 2015 18:10:57 +0100 Message-ID: <87si4kxtge.fsf@T420.taylan> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:55152) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZuO3y-0006NK-6k for guix-devel@gnu.org; Thu, 05 Nov 2015 12:11:03 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZuO3w-0002JB-Jk for guix-devel@gnu.org; Thu, 05 Nov 2015 12:11:02 -0500 Received: from mail-wi0-x233.google.com ([2a00:1450:400c:c05::233]:36342) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZuO3w-0002J0-AK for guix-devel@gnu.org; Thu, 05 Nov 2015 12:11:00 -0500 Received: by wimw2 with SMTP id w2so14610039wim.1 for ; Thu, 05 Nov 2015 09:10:59 -0800 (PST) Received: from T420.taylan ([2a02:908:c32:4740:221:ccff:fe66:68f0]) by smtp.gmail.com with ESMTPSA id q125sm9387246wmd.5.2015.11.05.09.10.57 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 05 Nov 2015 09:10:58 -0800 (PST) List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org To: guix-devel@gnu.org --=-=-= Content-Type: text/plain This reduces memory and CPU usage (unless you only have one CPU core), and reduces the time needed to compile, during 'guix pull'. Memory: It used to use as many guile processes as you have cores, each consuming about 100-150 MB memory. Now it uses a single guile process whose memory consumption grows linearly until it reaches almost 300 MB. CPU: It used to max out every CPU core, now just one. :-) Time: It used to take ~18 minutes on my machine, now less than 3. The code is vastly simplified as well, as you see. I'm not sure if there's a good way to parallelize it though, and it's somewhat hacky to keep all the modules in memory instead of spawning a clean guile to compile each file... --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: inline; filename=0001-build-pull-Compile-.scm-files-in-one-process.patch Content-Transfer-Encoding: quoted-printable >From fd5d9bf77fd38fad354d66c31e57bc9bbc86481f Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Taylan=3D20Ulrich=3D20Bay=3DC4=3DB1rl=3DC4=3DB1/Kammer?=3D Date: Thu, 5 Nov 2015 16:21:48 +0100 Subject: [PATCH] build: pull: Compile .scm files in one process. * guix/build/pull.scm (call-with-process): Removed procedure. (report-build-progress): Removed procedure. (p-for-each): Removed procedure. (build-guix): Don't create subprocesses to compile the .scm files. --- guix/build/pull.scm | 119 +++++++++++-------------------------------------= ---- 1 file changed, 25 insertions(+), 94 deletions(-) diff --git a/guix/build/pull.scm b/guix/build/pull.scm index 281be23..0aa14eb 100644 --- a/guix/build/pull.scm +++ b/guix/build/pull.scm @@ -33,75 +33,10 @@ ;;; ;;; Code: =20 -(define (call-with-process thunk) - "Run THUNK in a separate process that will return 0 if THUNK terminates -normally, and 1 if an exception is raised." - (match (primitive-fork) - (0 - (catch #t - (lambda () - (thunk) - (primitive-exit 0)) - (lambda (key . args) - (print-exception (current-error-port) #f key args) - (primitive-exit 1)))) - (pid - #t))) - -(define* (report-build-progress total completed cont - #:optional (log-port (current-error-port))) - "Report that COMPLETED out of TOTAL files have been completed, and call -CONT." - (display #\cr log-port) - (format log-port "compiling...\t~5,1f% of ~d files" ;FIXME: i18n - (* 100. (/ completed total)) total) - (force-output log-port) - (cont)) - -(define* (p-for-each proc lst - #:optional (max-processes (current-processor-count)) - #:key (progress report-build-progress)) - "Invoke PROC for each element of LST in a separate process, using up to -MAX-PROCESSES processes in parallel. Call PROGRESS at each step, passing = it -the continuation. Raise an error if one of the processes exit with non-ze= ro." - (define total - (length lst)) - - (define (wait-for-one-process) - (match (waitpid WAIT_ANY) - ((_ . status) - (unless (zero? (status:exit-val status)) - (error "process failed" proc status))))) - - (let loop ((lst lst) - (running 0) - (completed 0)) - (match lst - (() - (or (zero? running) - (let ((running (- running 1)) - (completed (+ completed 1))) - (wait-for-one-process) - (progress total completed - (lambda () - (loop lst running completed)))))) - ((head . tail) - (if (< running max-processes) - (let ((running (+ 1 running))) - (call-with-process (cut proc head)) - (progress total completed - (lambda () - (loop tail running completed)))) - (let ((running (- running 1)) - (completed (+ completed 1))) - (wait-for-one-process) - (progress total completed - (lambda () - (loop lst running completed))))))))) - (define* (build-guix out source #:key gcrypt - (debug-port (%make-void-port "w"))) + (debug-port (%make-void-port "w")) + (log-port (current-error-port))) "Build and install Guix in directory OUT using SOURCE, a directory containing the source code. Write any debugging output to DEBUG-PORT." (setvbuf (current-output-port) _IOLBF) @@ -130,33 +65,29 @@ containing the source code. Write any debugging outpu= t to DEBUG-PORT." (set! %load-path (cons out %load-path)) (set! %load-compiled-path (cons out %load-compiled-path)) =20 - ;; Compile the .scm files. Do that in independent processes, =C3=A0 la - ;; 'make -j', to work around (FIXME). - ;; This ensures correctness, but is overly conservative and slow. - ;; The solution initially implemented (and described in the bug - ;; above) was slightly faster but consumed memory proportional to the - ;; number of modules, which quickly became unacceptable. - (p-for-each (lambda (file) - (let ((go (string-append (string-drop-right file 4) - ".go"))) - (format debug-port "~%compiling '~a'...~%" file) - (parameterize ((current-warning-port debug-port)) - (compile-file file - #:output-file go - #:opts - %auto-compilation-options)))) - - (filter (cut string-suffix? ".scm" <>) - - ;; Build guix/*.scm before gnu/*.scm to speed - ;; things up. - (sort (find-files out "\\.scm") - (let ((guix (string-append out "/guix")) - (gnu (string-append out "/gnu"))) - (lambda (a b) - (or (and (string-prefix? guix a) - (string-prefix? gnu b)) - (string (FIXME). + (let* ((files (filter (cut string-suffix? ".scm" <>) + (find-files out "\\.scm"))) + (total (length files))) + (let loop ((file (car files)) + (files (cdr files)) + (completed 0)) + (display #\cr log-port) + (format log-port "compiling...\t~5,1f% of ~d files" ;FIXME: i18n + (* 100. (/ completed total)) total) + (force-output log-port) + (let ((go (string-append (string-drop-right file 4) + ".go"))) + (format debug-port "~%compiling '~a'...~%" file) + (parameterize ((current-warning-port debug-port)) + (compile-file file + #:output-file go + #:opts + %auto-compilation-options) + (load-compiled go))) + (unless (null? files) + (loop (car files) (cdr files) (+ 1 completed)))))) =20 ;; Remove the "fake" (guix config). (delete-file (string-append out "/guix/config.scm")) --=20 2.5.0 --=-=-=--