From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id iA72J9hs4l+XRAAA0tVLHw (envelope-from ) for ; Tue, 22 Dec 2020 22:02:00 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id GPTPI9hs4l+TcgAAB5/wlQ (envelope-from ) for ; Tue, 22 Dec 2020 22:02:00 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 771119401BC for ; Tue, 22 Dec 2020 22:01:59 +0000 (UTC) Received: from localhost ([::1]:34788 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1krpjC-0001N4-8n for larch@yhetil.org; Tue, 22 Dec 2020 17:01:58 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:46896) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1krpid-0001Mu-U0 for guix-devel@gnu.org; Tue, 22 Dec 2020 17:01:23 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]:37704) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1krpic-0006lQ-1a for guix-devel@gnu.org; Tue, 22 Dec 2020 17:01:23 -0500 Received: from [2a01:e0a:1d:7270:af76:b9b:ca24:c465] (port=45256 helo=ribbon) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1krpiY-0004Np-SI for guix-devel@gnu.org; Tue, 22 Dec 2020 17:01:19 -0500 From: =?utf-8?Q?Ludovic_Court=C3=A8s?= To: Guix Devel Subject: Identical files across subsequent package revisions X-URL: http://www.fdn.fr/~lcourtes/ X-Revolutionary-Date: 2 =?utf-8?Q?Niv=C3=B4se?= an 229 de la =?utf-8?Q?R?= =?utf-8?Q?=C3=A9volution?= X-PGP-Key-ID: 0x090B11993D9AEBB5 X-PGP-Key: http://www.fdn.fr/~lcourtes/ludovic.asc X-PGP-Fingerprint: 3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5 X-OS: x86_64-pc-linux-gnu Date: Tue, 22 Dec 2020 23:01:17 +0100 Message-ID: <87wnx9wlea.fsf@gnu.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-BeenThere: guix-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list 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+larch=yhetil.org@gnu.org Sender: "Guix-devel" X-Migadu-Flow: FLOW_IN X-Migadu-Spam-Score: -2.82 Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=pass (policy=none) header.from=gnu.org; spf=pass (aspmx1.migadu.com: domain of guix-devel-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-devel-bounces@gnu.org X-Migadu-Queue-Id: 771119401BC X-Spam-Score: -2.82 X-Migadu-Scanner: scn1.migadu.com X-TUID: 3p2zsX252pqg --=-=-= Content-Type: text/plain Hello Guix! Every time a package changes, we end up downloading complete substitutes for itself and for all its dependents, even though we have the intuition that a large fraction of the files in those store items are unchanged. I wanted to evaluate that by looking at store items corresponding to subsequent revisions of a package (be it different versions or rebuilds induced by dependencies), and this is what the program below does. Here are preliminary results for a few packages: --=-=-= Content-Type: image/png Content-Disposition: inline; filename=t.png Content-Transfer-Encoding: base64 Content-Description: the graph iVBORw0KGgoAAAANSUhEUgAABt0AAAGpCAIAAABu125tAAAABmJLR0QA/wD/AP+gvaeTAAAgAElE QVR4nOzdeVxU9f7H8e+wCQwDmowbo+AWKS7jUrZItmlKkl4hgcrlUpqaPzBcq5sW6k0t08iFlFQs Q4zMlMjr1XLrXu+VTMhKvSqjhiJpiAOiDDC/P87vNw/uwAwjDGdieD3/mvnOd/mcM2eGfHfOHIXR aBQAAAAAAAAAICMXRxcAAAAAAAAAoNkhlwQAAAAAAAAgN3JJAAAAAAAAAHIjlwQAAAAAAAAgN3JJ AAAAAAAAAHIjlwQAAAAAAAAgN3JJAAAAAAAAAHIjlwQAAAAAAAAgN3JJAADQWP79738nJCRERESE hYU999xzb7311rFjx6SXPvnkk6eeeqp+01Yf25B5zIb/8MMPaWlp9Z7KuSUnJ0dGRjq6Cvtw1Bvt TPtQYsunr4GfUAAA4NzIJQEAQKP4+uuvX331VSFEbGzsnDlzwsPDi4uL//Wvf0mvtmzZMjAwsH4z N2SslanIJZsJ3mh7seWTaMdPKwAAcD5uji4AAAA4p4yMjM6dO7/77rsuLv/3/0Gjo6MrKiqkxyNH jhw5cmT9Zm7IWBODweDu7m6XqexIqsrRVaC5aODxZsvH54/2EQMAAH8o5JIAAKBRlJSUdOvWzRRK Stzc/u+/PT755JO0tLSvvvpKCJGcnLx3794lS5Z88MEHp0+f9vf3nzRp0sMPP7xr16709PTr16/3 7Nlz9uzZarW65tjq8vLytmzZ8tNPPxUVFfn7+w8aNCg2NlapVEqvSqu89tprH3300blz50aMGBEX F2eaavXq1du3bxdCPP7440IIf3//uLi4+fPnr1279u677zYtMXv27NLS0jVr1tzR0kKIc+fObdq0 KTc39/bt223atHnyySefffZZS1UJIbKzs1NTU8+cOePm5tanT5/JkyebTjq7cuXKunXrcnJy9Hp9 y5Ytg4OD582b5+3tbandrFRL3f76179evHhx7dq1pp4JCQk+Pj6JiYmmlp9//nn16tVnz55t2bJl RETEM888Y31OacM3bNiQm5trMBi6d+8+adKk3r17myY8cODApk2bLl++3L59+4kTJ2ZmZnp4eCxe vFgIUWc9VmaW9uqCBQs+/PDDs2fP+vv7/+lPfxozZowQouYbnZ6ebraLpOGzZ8/+6KOPLly4YLax 9X6vzezevfu9994bP378888/X+ec+/fvT01NvXz5ckBAwMSJE3fu3GnaUXXu5JqbVvN4szTD/v37 Fy5cuH79+i5dupgmmTdv3u+//75u3brqn0RLx4DZp9XKgW3lXbN+jAEAgKaLXBIAADSKnj17fvfd d5999tkjjzxiihQtuXXr1qJFi0aNGhUVFbVz585FixZFRkb+9NNPL774osFgWLdu3dtvv/3ee+9Z n+TKlSvt27cfMmSIr69vQUHBp59+eubMmffff9/UoaSkJCkpaerUqV26dDEYDNXHjh8/vqqqas+e PevXrxdCuLq6tm7dWq1WZ2ZmJiQkSH3y8/N/+OEH01Pblz59+vSMGTM6dOgwbdo0tVqdn59/7tw5 K1V9//33r776qlarfeONN27durVp06a4uLh169a1bdtWCPHWW2+Vl5fHxcW1bt362rVr2dnZ0lmo ltrN2NitprKyssTExJiYmI4dOx4+fDg5OdnT0zM8PNzKnGfPno2LiwsKCkpISPD29s7MzJw1a9YH H3wgRb3Hjx9fuHDh4MGDp02bVlxc/OGHHxoMhuopsBXWZ5b26qpVq6ZNm9axY8f9+/evXr1ao9Hc d999Nd/oWufX6/Vr166dM2dOYGDgwYMHV65cadrYhrzXJmlpaRs3bnzllVdGjBhR55zHjx9ftGhR aGjoyy+/XFxcvG7dutu3b3fv3t3GXWGm5vFmZYYHH3xQqVTu3bt38uTJ0vCioqJjx45NmjTJbFpb jivrB7aVd83G+QEAQJNDLgkAABrFyy+/XFRUlJycnJycrFar+/Xr9+STT2q12lo7l5WVTZ8+feDA gUKInj17RkZG7tu3b/PmzS1atBBC3Lx5MykpqaioqFWrVlZWvP/++++//37pcd++fbt27frSSy/l 5eV17txZajQYDPHx8f369as5VqVSKZVKhULRrl07U+PIkSO3bt06ZcoU6bSsr776ysvL67HHHrvT pT/88EMfH59Vq1Z5enoKIcwKqFnVxo0b27Ztu2TJEik169Wr17hx47Zu3RofH19RUXH69OmZM2c+ /PDDUmfpgaV2MzZ2q9WtW7cSEhKk0wz79+9fXFy8efPmsLAwo9Foac5169b5+fktX75c2vB77713 8uTJn3zyiXTOY2pqaufOnRcsWKBQKIQQgYGBU6ZMsbEY6zNLezUhISE4OFgIMWbMmN27d3/zzTf3 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 H/8G4DHeXylmAAAAAElFTkSuQmCC --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable It reads as follows: =E2=80=A2 Pairwise similarities of subsequent revisions of IceCat contain identical files representing ~10% of its total size (it=E2=80=99s the r= atio of the size of the identical files to the total file size.) The first bar represents similarity between the first and the second revision; the second bar shows the similarity between the second one and the third one, etc. =E2=80=A2 For Emacs, identical files between subsequent revisions represe= nt ~85% of its total size. Intuitively, this is because the package contains mostly source code (.el.gz files) and bytecode, which remains unchanged. =E2=80=A2 Diffoscope is written in Python so it=E2=80=99s similar to Emac= s: its .py file remain unchanged across revisions, and they represent ~60% of its total size. =E2=80=A2 GIMP has lots of headers, locale data, and icons that don=E2=80= =99t change. =E2=80=A2 For R, we see the effect of the upgrades to 4.0.2 and then 4.0.= 3, where similarity drops to ~25% instead of ~80% when changes are in dependencies. =E2=80=A2 For Open=C2=A0MPI, which is compiled C + headers, ~25% is share= d across revisions. The reason I=E2=80=99m looking at this is to understand how much would be g= ained in terms of bandwidth usage if we were able to avoid downloading individual files already in the store. It would seem to be rather encouraging. Below is the program that does that. It grabs revision history from data.guix.gnu.org, fetches nars from ci.guix.gnu.org, computes a =E2=80=9Cdigest=E2=80=9D (list of files along with their hash and size), co= mpares package digests pairwise, and plots the result with Guile-Charting. Example REPL session: --8<---------------cut here---------------start------------->8--- scheme@(similarities)> (pairwise-similarities (package-archive-contents "i= cecat" #:max 4)) updating substitutes from 'https://ci.guix.gnu.org'... 100.0% $86 =3D (17363915/196387883 11380615/98152193) scheme@(similarities)> (map exact->inexact $86) $87 =3D (0.08841642740249916 0.11594865740799087) [=E2=80=A6] scheme@(similarities)> ,pp (at-most 10 (package-instances "r-minimal") ) $100 =3D (#< version: "4.0.3" output: "/gnu/store/vv3ca1r= 5zw5y35xgkix4r80hdnncx52b-r-minimal-4.0.3"> #< version: "4.0.3" output: "/gnu/store/5dzad7nhhv3dvmap= 60d6gsj9ppflgzrd-r-minimal-4.0.3"> #< version: "4.0.3" output: "/gnu/store/01xi3sig314wgwa1= j9sxk37vl816mj74-r-minimal-4.0.3"> #< version: "4.0.2" output: "/gnu/store/nv7lqhnm0mncqwdp= kkhnlsgb562lcwff-r-minimal-4.0.2"> #< version: "4.0.2" output: "/gnu/store/w0izbm8q26dmyndh= v46xr7dgz1irai1z-r-minimal-4.0.2"> #< version: "4.0.2" output: "/gnu/store/yd83ibzxjrb7cgcc= 6d4smx4pqcdl8r3p-r-minimal-4.0.2"> #< version: "4.0.1" output: "/gnu/store/kpdh0lwlwcwfmmfz= qhwbi6j7m4zzxlmn-r-minimal-4.0.1"> #< version: "4.0.1" output: "/gnu/store/9x9nzzsiyn1q7g5m= yhgwjh0yx9js3nrj-r-minimal-4.0.1"> #< version: "4.0.0" output: "/gnu/store/ahbm2gsqc3420a23= pcwrxd4pdhl7rdpp-r-minimal-4.0.0"> #< version: "4.0.0" output: "/gnu/store/0sgqhj2628js419w= vw1vc1cw07wil7gr-r-minimal-4.0.0">) $101 =3D (#< version: "3.6.3" output: "/gnu/store/gmx6p0w= k3xbc9ylv83zfj855azgjxr0p-r-minimal-3.6.3"> #< version: "3.6.2" output: "/gnu/store/dnb6fzp5295fcda6= 6dnjk2y51mcva20f-r-minimal-3.6.2"> #< version: "3.6.1" output: "/gnu/store/gd6sm42b6fr1qgyp= 6p1zp3z4aavxwyk2-r-minimal-3.6.1"> #< version: "3.6.1" output: "/gnu/store/lpmfhxys3vsaqmqv= j85r344ygfmlmlbg-r-minimal-3.6.1"> #< version: "3.6.1" output: "/gnu/store/4413h13v7zrb7rp9= lvyp2gfx2laj60wm-r-minimal-3.6.1"> #< version: "3.6.1" output: "/gnu/store/zm5pl1y0wmh3c845= 498pbjvrzrm6sb07-r-minimal-3.6.1"> #< version: "3.6.1" output: "/gnu/store/xrv7y4xgrdrsx5qb= a7144cpw69qc5f0j-r-minimal-3.6.1"> #< version: "3.6.0" output: "/gnu/store/cbwhhqv69xi3k3g2= 5dcfhwjkkf2427rp-r-minimal-3.6.0"> #< version: "3.6.0" output: "/gnu/store/69k46yr70zkzzz9v= 18wl7sxasha5m0a5-r-minimal-3.6.0"> #< version: "3.6.0" output: "/gnu/store/71w0383x0hay6ng1= zaddz59by3g0gxaf-r-minimal-3.6.0"> #< version: "3.6.0" output: "/gnu/store/m317mg8faxp9qn94= 9dnv1xgsxyw57s3x-r-minimal-3.6.0"> #< version: "3.5.3" output: "/gnu/store/33qdfplk4riig77v= qg758m1zll16n6f0-r-minimal-3.5.3"> #< version: "3.5.3" output: "/gnu/store/g8gkrcxn49yj8zjr= 59l7y4k7wgar5brq-r-minimal-3.5.3"> #< version: "3.5.1" output: "/gnu/store/vrgbyvnx7b1sva2i= j5a1gwrkbfwmg3lm-r-minimal-3.5.1"> #< version: "3.5.1" output: "/gnu/store/4fzw7s0cv2zbixw4= wb58zq6lkd97ghnf-r-minimal-3.5.1"> #< version: "3.5.1" output: "/gnu/store/yb5048dr40nbmyfa= 1ar4hfiy3kd06v4c-r-minimal-3.5.1">) scheme@(similarities)> (similarity-chart '("icecat" "gimp" "openmpi" "emacs= " "diffoscope" "r-minimal") "/tmp/t.png" #:max 8) updating substitutes from 'https://ci.guix.gnu.org'... 100.0% $102 =3D # --8<---------------cut here---------------end--------------->8--- Thoughts? :-) Ludo=E2=80=99. --=-=-= Content-Type: text/plain; charset=utf-8 Content-Disposition: inline; filename=similarities.scm Content-Transfer-Encoding: quoted-printable Content-Description: the code ;;; Copyright =C2=A9 2020 Ludovic Court=C3=A8s ;;; Hereby placed under the GNU General Public License, version 3 or later. (define-module (similarities) #:use-module (json) #:use-module ((gcrypt hash) #:select (open-sha256-port)) #:use-module ((guix store) #:select (%default-substitute-urls)) #:use-module ((guix progress) #:hide (dump-port*)) #:use-module (guix http-client) #:use-module (guix serialization) #:use-module (guix scripts substitute) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) #:use-module (srfi srfi-11) #:use-module (rnrs bytevectors) #:use-module (charting) #:use-module (ice-9 match)) ;;; ;;; Data Service client. ;;; (define-json-mapping make-package-instance package-instance? json->package-instance (version package-instance-version) (output package-instance-output "derivation")) (define %data-service-base-url (make-parameter "https://data.guix.gnu.org")) (define* (package-instances package #:key (branch "master")) "Return a list of representing instances of PACKAGE ov= er time known to the Data Service." (match (assoc-ref (json->scm (http-fetch (string-append (%data-service-base-url) "/repository/1/branch/" branch "/package/" package "/output-history.json"))) "derivations") (#(lst ...) (map json->package-instance lst)) (#f #f))) ;;; ;;; Similarity measurement. ;;; (define (port-sha256* port size) ;from (guix scripts challeng= e) ;; Like 'port-sha256', but limited to SIZE bytes. (let-values (((out get) (open-sha256-port))) (dump-port* port out size) (close-port out) (get))) (define-record-type (file-info name type size sha256) file-info? (name file-info-name) (type file-info-type) (size file-info-size) (sha256 file-info-sha256)) (define (archive-contents port) "Return a list of records from the nar read from PORT." ;; As in (guix scripts challenge), but return records that ;; include file size and ignore symlinks. (fold-archive (lambda (file type contents result) (match type ((or 'regular 'executable) (match contents ((port . size) (cons (file-info file type size (port-sha256* port size)) result)))) ('directory result) ('directory-complete result) ('symlink result))) '() port "")) (define (narinfo-contents narinfo) ;from (guix scripts challeng= e) "Fetch the nar described by NARINFO and return a list representing the fi= le it contains." ((@@ (guix scripts challenge) call-with-nar) narinfo archive-contents)) (define (at-most max-length lst) ;from (guix scripts substitut= e) "If LST is shorter than MAX-LENGTH, return it and the empty list; otherwi= se return its MAX-LENGTH first elements and its tail." (let loop ((len 0) (lst lst) (result '())) (match lst (() (values (reverse result) '())) ((head . tail) (if (>=3D len max-length) (values (reverse result) lst) (loop (+ 1 len) tail (cons head result))))))) (define* (package-archive-contents package #:key (max 10) (substitute-urls %default-substitute-url= s)) "Look at the MAX latest instances of PACKAGE, fetch them, and return a summary of their contents as returned by 'narinfo-contents'." (let ((instances (at-most max (package-instances package)))) (map narinfo-contents (lookup-narinfos (first substitute-urls) (map package-instance-output instances))))) (define (similarity contents1 contents2) "Return two values: the ratio of identical bytes between CONTENTS2 and CONTENTS2, and the ratio of identical files." (define (matches name) (lambda (info) (string=3D? (file-info-name info) name))) (let ((files (delete-duplicates (append (map file-info-name contents1) (map file-info-name contents2))))) (let loop ((files files) (seen 0) (identical 0) (seen-bytes 0) (identical-bytes 0)) (match files (() (values (/ identical-bytes seen-bytes) (/ identical seen))) ((head . tail) (let ((file1 (find (matches head) contents1)) (file2 (find (matches head) contents2))) (cond ((not file1) (loop tail (+ seen 1) identical (+ seen-bytes (file-info-size file2)) identical-bytes)) ((not file2) (loop tail (+ seen 1) identical (+ seen-bytes (file-info-size file1)) identical-bytes)) (else (let ((identical? (and (=3D (file-info-size file1) (file-info-size file2)) (bytevector=3D? (file-info-sha256 file1) (file-info-sha256 file2))))) (loop tail (+ seen 1) (if identical? (+ identical 1) identical) (+ seen-bytes (max (file-info-size file1) (file-info-size file2))) (if identical? (+ identical-bytes (file-info-size file1)) identical-bytes))))))))))) (define (pairwise-similarities contents) (let loop ((contents contents) (similarities '())) (match contents ((or () (_)) (reverse similarities)) ((a b . tail) (loop (cons b tail) (cons (similarity a b) similarities)))))) (define* (similarity-chart packages file #:key (max 20)) (make-bar-chart "Similarity across subsequent package revisions" (map (lambda (package) (let* ((contents (package-archive-contents package #:max max)) (similarities (pairwise-similarities contents)) (labels (iota (length similarities)))) `(,package ,@(map (lambda (label ratio) `(,(* ratio 100.) ,(number->string label))) labels similarities)))) packages) #:chart-params '(#:x-axis-label "package" #:y-axis-label "similarity ratio (%)") #:write-to-png file)) --=-=-=--