guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

The size of ‘.go’ files


From: Ludovic Courtès
Subject: The size of ‘.go’ files
Date: Fri, 05 Jun 2020 22:50:10 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.3 (gnu/linux)

Hello Guix!

On IRC there was a discussion about the size of ‘.go’ files.  The
discussion came from this observation:

--8<---------------cut here---------------start------------->8---
$ guix size $(readlink -f /run/current-system) | head -5
store item                                                       total    self
/gnu/store/4d0p06xgaw8lqa9db0d6728kkba8bizj-qemu-5.0.0            1651.6   
745.2  18.8%
/gnu/store/abiva5ivq99x30r2s9pa3jj0pv9g16sv-guix-1.1.0-4.bdc801e   468.0   
268.8   6.8%
/gnu/store/111zp1qyind7hsnvrm5830jhankmx4ls-linux-libre-5.4.43     243.6   
243.6   6.2%
/gnu/store/skxkrhgn9z0fg9hmnbcyfdgzs5w4ryrr-llvm-9.0.1             199.9   
128.5   3.2%
--8<---------------cut here---------------end--------------->8---

On disk, those .go files take quite a bit of space (I hear you Btrfs
people, don’t say it! :-)).

The code snippet below sorts the ELF sections of a .go file by size; for
‘python-xyz.go’, I get this:

--8<---------------cut here---------------start------------->8---
$13 = ((".rtl-text" . 3417108)
 (".guile.arities" . 1358536)
 (".data" . 586912)
 (".rodata" . 361599)
 (".symtab" . 117000)
 (".debug_line" . 97342)
 (".debug_info" . 54519)
 (".guile.frame-maps" . 47114)
 ("" . 1344)
 (".guile.arities.strtab" . 681)
 ("" . 232)
 (".shstrtab" . 229)
 (".dynamic" . 112)
 (".debug_str" . 87)
 (".strtab" . 75)
 (".debug_abbrev" . 65)
 (".guile.docstrs.strtab" . 1)
 ("" . 0)
 (".guile.procprops" . 0)
 (".guile.docstrs" . 0)
 (".debug_loc" . 0))
scheme@(guile-user)> (stat:size (stat go))
$14 = 6083445
--8<---------------cut here---------------end--------------->8---

More than half of those 6 MiB is code, and more than 1 MiB is
“.guile.arities” (info "(guile) Object File Format"), which is
surprisingly large; presumably the file only contains thunks (the
‘thunked’ fields of <package>).

Stripping the .debug_* sections (if that works) clearly wouldn’t help.

So I guess we could generate less code (reduce ‘.rtl-text’), perhaps by
tweaking ‘define-record-type*’, but I have little hope there.

We could also investigate where “.guile.arities” could be made denser,
or use fewer thunked fields in <package>.  Currently arity info takes
7x4 = 28 bytes per procedure as documented in (system vm assembler).
With an extra flag we could perhaps save 8 bytes for the simple case
where nopt = 0, nreq is small, and other flags are zero.

But anyway, currently there are (1358536 - 4) / 28 = 48K arity headers
in this file.  However, the file contains 970 packages, so we’re talking
about ~50 procedures per package, even though there are only 5 thunked
fields.  Weird!  Maybe I’m missing something.

But wait, that was with 3.0.2 and -O1.

With 3.0.3-to-be and -O1, python-xyz.go weighs in at 3.4 MiB instead of
5.9 MiB!  Here’s the section size distribution:

--8<---------------cut here---------------start------------->8---
$4 = ((".rtl-text" . 2101168)
 (".data" . 586392)
 (".rodata" . 360703)
 (".guile.arities" . 193106)
 (".symtab" . 117000)
 (".debug_line" . 76685)
 (".debug_info" . 53513)
 ("" . 1280)
 (".guile.arities.strtab" . 517)
 ("" . 232)
 (".shstrtab" . 211)
 (".dynamic" . 96)
 (".debug_str" . 87)
 (".strtab" . 75)
 (".debug_abbrev" . 56)
 (".guile.docstrs.strtab" . 1)
 ("" . 0)
 (".guile.procprops" . 0)
 (".guile.docstrs" . 0)
 (".debug_loc" . 0))
scheme@(guile-user)> (stat:size (stat go))
$5 = 3519323
--8<---------------cut here---------------end--------------->8---

“.rtl-text” is 38% smaller and “.guile.arities” is almost a tenth of
what it was.

Something’s going on here!  Thoughts?

Ludo’.

(use-modules (system vm elf)
             (rnrs io ports)
             (ice-9 match))

(define go
  (search-path %load-compiled-path "gnu/packages/python-xyz.go"))

(define elf
  (parse-elf (call-with-input-file go get-bytevector-all)))

(define (elf-section-name-as-string elf section)
  (let ((off (elf-section-offset
              (list-ref (elf-sections elf)
                        (elf-shstrndx elf)))))
    (string-table-ref (elf-bytes elf)
                      (+ off (elf-section-name section)))))

(sort (map (lambda (section)
             (cons (elf-section-name-as-string elf section)
                   (elf-section-size section)))
           (elf-sections elf))
      (match-lambda*
        (((name1 . size1) (name2 . size2))
         (> size1 size2))))

reply via email to

[Prev in Thread] Current Thread [Next in Thread]