Zastąpienie ld złotem - jakieś doświadczenie?


81

Czy ktoś próbował użyć goldzamiast ld?

gold obiecuje być znacznie szybszy niż ld, więc może pomóc przyspieszyć cykle testowe dla dużych aplikacji C ++, ale czy może być używany jako zamiennik ld?

Czy mogę gcc/ g++bezpośrednio zadzwonić gold.?

Czy są jakieś znane błędy lub problemy?

Chociaż goldod jakiegoś czasu jest częścią binutils GNU, w sieci nie znalazłem prawie żadnych „historii sukcesu” ani nawet „poradników”.

( Aktualizacja: dodane linki do złota i wpis na blogu wyjaśniający to )

Odpowiedzi:


53

W tej chwili kompiluje większe projekty na Ubuntu 10.04. Tutaj możesz go łatwo zainstalować i zintegrować z binutils-goldpakietem (jeśli usuniesz ten pakiet, otrzymasz stary ld). Gcc automatycznie użyje wtedy złota.

Niektóre doświadczenia:

  • złoto nie przeszukuje /usr/local/lib
  • gold nie zakłada bibliotek takich jak pthread czy rt, musiało dodawać je ręcznie
  • jest szybszy i wymaga mniej pamięci (to późniejsze jest ważne w przypadku dużych projektów C ++ z dużym przyspieszeniem itp.)

Co nie działa: nie może kompilować jądra, a zatem nie ma modułów jądra. Ubuntu robi to automatycznie za pośrednictwem DKMS, jeśli aktualizuje zastrzeżone sterowniki, takie jak fglrx. To się nie powiedzie z ld-gold(musisz usunąć złoto, zrestartować DKMS, ponownie zainstalować ld-gold.


Dzięki, myślę, że spróbuję - ograniczenia, o których wspomniałeś, nie wydają się być problemem w moim przypadku.
IanH

+1: dzięki za podzielenie się doświadczeniem. A co z wydajnością?
neuro-

9
jest znacznie szybszy, zwłaszcza przy łączeniu razem ogromnych bibliotek statycznych w jeden plik binarny, ale nie wykonaliśmy żadnych pomiarów.
nob

2
@neuro Moje pomiary dotyczyły łączenia wielu obiektów i plików .a w zestaw ~ 30 plików .so (jeden duży, pozostałe małe) i 1 plik wykonywalny dla znaczącej aplikacji komercyjnej. Mierząc tylko czas łącza i uruchamiając make w trybie szeregowym, uzyskałem łączny czas 22,48 sekundy z ld w porównaniu do 16,24 sekundy ze złotem, co daje poprawę o 6,24 sekundy na kompilację. Jeśli jednak uruchomię make równolegle z 8 procesorami, całkowita różnica to tylko 1,42 sekundy na kompilację. Całkowite użycie pamięci poprawiło się o 42%, niezależnie od równoległości produkcji. YMMV.
metal

@metal: wielkie dzięki za dane. Poprawa wykorzystania pamięci wygląda świetnie, ldjest tak chciwa.
neuro-

40

Ponieważ zajęło mi trochę czasu, zanim dowiedziałem się, jak selektywnie używać złota (tj. Nie używać łącza symbolicznego w całym systemie), opublikuję tutaj rozwiązanie. Opiera się na http://code.google.com/p/chromium/wiki/LinuxFasterBuilds#Linking_using_gold .

  1. Utwórz katalog, w którym możesz umieścić złoty skrypt kleju. Używam ~/bin/gold/.
  2. Umieść tam następujący skrypt kleju i nazwij go ~/bin/gold/ld:

    #!/bin/bash
    gold "$@"
    

    Oczywiście, sprawiają, że wykonywalny chmod a+x ~/bin/gold/ld.

  3. Zmień swoje połączenia do gcccelu gcc -B$HOME/bin/gold, co czyni gcc spojrzeć w danym katalogu programów pomocniczych, jak ldi w ten sposób wykorzystuje skrypt kleju zamiast systemu default ld.


1
To jest potrzebne dla jakiego systemu operacyjnego? Jak nikt nie powiedział w swojej odpowiedzi, dla Ubuntu po prostu zainstaluj złoty pakiet binutils, a kompilator od razu go użyje. To samo dotyczy openSuse.
usr1234567

8
Tak, dość łatwo jest wymienić ld w całym systemie. Moja odpowiedź była szczególnie ukierunkowana na to, jak selektywnie używać złota. Myślę, że w takim przypadku jest to konieczne dla każdego systemu operacyjnego.
Tilman Vogel

1
@vidstige Tak, zaletą skryptu jest to, że szuka goldw PATH. W przypadku łącza symbolicznego należałoby wskazać pełną ścieżkę.
Tilman Vogel,

18

Czy gcc / g ++ może bezpośrednio wywołać złoto?

Aby uzupełnić odpowiedzi: istnieje opcja gcc-fuse-ld=gold (zobacz dokumentację gcc ). Chociaż, AFAIK, możliwe jest skonfigurowanie gcc podczas kompilacji w taki sposób, że opcja nie będzie miała żadnego efektu.


5
-fuse-ld=goldnie jest kompletna. Jeśli musisz użyć -Wl,-fuse-ld=goldtego, co jest używane w czasie łącza.
Nawaz

6
@Nawaz Nie, -Wl,służy do przekazywania opcji bezpośrednio do ld; aby użyć innego konsolidatora, musisz o tym powiedzieć gcc. Proszę zapoznać się z dok .
calandoa

11

Jako programista Samby od kilku lat używam złotego linkera prawie wyłącznie w systemach Ubuntu, Debian i Fedora. Moja ocena:

  • złoto jest wielokrotnie (odczuwalne: 5-10 razy) szybsze niż klasyczny linker.
  • Początkowo było kilka problemów, ale zniknęły od mniej więcej Ubuntu 12.04.
  • Złoty linker znalazł nawet pewne problemy z zależnościami w naszym kodzie, ponieważ pod względem niektórych szczegółów wydaje się być bardziej poprawny niż klasyczny. Zobacz np. Ten commit w Sambie .

Nie używałem złota selektywnie, ale korzystałem z linków symbolicznych lub mechanizmu alternatywnego, jeśli dystrybucja to zapewnia.


9

Możesz linkować lddo gold(w lokalnym katalogu binarnym, jeśli ldzainstalowałeś, aby uniknąć nadpisywania):

ln -s `which gold` ~/bin/ld

lub

ln -s `which gold` /usr/local/bin/ld

6

Minimalny syntetyczny benchmark: LD vs złoto vs LLVM LLD

Wynik:

  • złoto było około 3x do 4x szybsze dla wszystkich wartości, których próbowałem używać -Wl,--threads -Wl,--thread-count=$(nproc)do włączania wielowątkowości
  • LLD było około 2x szybsze niż złoto!

Przetestowano na:

  • Ubuntu 20.04, GCC 9.3.0, binutils 2.34, sudo apt install lldLLD 10
  • Laptop Lenovo ThinkPad P51, procesor Intel Core i7-7820HQ (4 rdzenie / 8 wątków), 2x pamięć RAM Samsung M471A2K43BB1-CRC (2x 16GiB), dysk SSD Samsung MZVLB512HAJQ-000L7 (3000 MB / s).

Uproszczony opis parametrów odniesienia:

  • 1: liczba plików obiektowych dostarczających symbole
  • 2: liczba symboli na plik obiektu dostawcy symboli
  • 3: liczba plików obiektowych wykorzystujących wszystkie dostarczone symbole symboli

Wyniki dla różnych parametrów wzorcowych:

10000 10 10
nogold:  wall=4.35s user=3.45s system=0.88s 876820kB
gold:    wall=1.35s user=1.72s system=0.46s 739760kB
lld:     wall=0.73s user=1.20s system=0.24s 625208kB

1000 100 10
nogold:  wall=5.08s user=4.17s system=0.89s 924040kB
gold:    wall=1.57s user=2.18s system=0.54s 922712kB
lld:     wall=0.75s user=1.28s system=0.27s 664804kB

100 1000 10
nogold:  wall=5.53s user=4.53s system=0.95s 962440kB
gold:    wall=1.65s user=2.39s system=0.61s 987148kB
lld:     wall=0.75s user=1.30s system=0.25s 704820kB

10000 10 100
nogold:  wall=11.45s user=10.14s system=1.28s 1735224kB
gold:    wall=4.88s user=8.21s system=0.95s 2180432kB
lld:     wall=2.41s user=5.58s system=0.74s 2308672kB

1000 100 100
nogold:  wall=13.58s user=12.01s system=1.54s 1767832kB
gold:    wall=5.17s user=8.55s system=1.05s 2333432kB
lld:     wall=2.79s user=6.01s system=0.85s 2347664kB

100 1000 100
nogold:  wall=13.31s user=11.64s system=1.62s 1799664kB
gold:    wall=5.22s user=8.62s system=1.03s 2393516kB
lld:     wall=3.11s user=6.26s system=0.66s 2386392kB

Oto skrypt, który generuje wszystkie obiekty do testów odsyłaczy:

generować obiekty

#!/usr/bin/env bash
set -eu

# CLI args.

# Each of those files contains n_ints_per_file ints.
n_int_files="${1:-10}"
n_ints_per_file="${2:-10}"

# Each function adds all ints from all files.
# This leads to n_int_files x n_ints_per_file x n_funcs relocations.
n_funcs="${3:-10}"

# Do a debug build, since it is for debug builds that link time matters the most,
# as the user will be recompiling often.
cflags='-ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic'

# Cleanup previous generated files objects.
./clean

# Generate i_*.c, ints.h and int_sum.h
rm -f ints.h
echo 'return' > int_sum.h
int_file_i=0
while [ "$int_file_i" -lt "$n_int_files" ]; do
  int_i=0
  int_file="${int_file_i}.c"
  rm -f "$int_file"
  while [ "$int_i" -lt "$n_ints_per_file" ]; do
    echo "${int_file_i} ${int_i}"
    int_sym="i_${int_file_i}_${int_i}"
    echo "unsigned int ${int_sym} = ${int_file_i};" >> "$int_file"
    echo "extern unsigned int ${int_sym};" >> ints.h
    echo "${int_sym} +" >> int_sum.h
    int_i=$((int_i + 1))
  done
  int_file_i=$((int_file_i + 1))
done
echo '1;' >> int_sum.h

# Generate funcs.h and main.c.
rm -f funcs.h
cat <<EOF >main.c
#include "funcs.h"

int main(void) {
return
EOF
i=0
while [ "$i" -lt "$n_funcs" ]; do
  func_sym="f_${i}"
  echo "${func_sym}() +" >> main.c
  echo "int ${func_sym}(void);" >> funcs.h
  cat <<EOF >"${func_sym}.c"
#include "ints.h"

int ${func_sym}(void) {
#include "int_sum.h"
}
EOF
  i=$((i + 1))
done
cat <<EOF >>main.c
1;
}
EOF

# Generate *.o
ls | grep -E '\.c$' | parallel --halt now,fail=1 -t --will-cite "gcc $cflags -c -o '{.}.o' '{}'"

GitHub upstream .

Zauważ, że generowanie pliku obiektowego może być dość powolne, ponieważ każdy plik C może być dość duży.

Biorąc pod uwagę dane wejściowe typu:

./generate-objects [n_int_files [n_ints_per_file [n_funcs]]]

generuje:

main.c

#include "funcs.h"

int main(void) {
    return f_0() + f_1() + ... + f_<n_funcs>();
}

f_0.c, f_1.c, ..., f_<n_funcs>.c

extern unsigned int i_0_0;
extern unsigned int i_0_1;
...
extern unsigned int i_1_0;
extern unsigned int i_1_1;
...
extern unsigned int i_<n_int_files>_<n_ints_per_file>;

int f_0(void) {
    return
    i_0_0 +
    i_0_1 +
    ...
    i_1_0 +
    i_1_1 +
    ...
    i_<n_int_files>_<n_ints_per_file>
}

0.c, 1.c, ..., <n_int_files>.c

unsigned int i_0_0 = 0;
unsigned int i_0_1 = 0;
...
unsigned int i_0_<n_ints_per_file> = 0;

który prowadzi do:

n_int_files x n_ints_per_file x n_funcs

relokacje na link.

Następnie porównałem:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic               -o main *.o
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -fuse-ld=gold -Wl,--threads -Wl,--thread-count=`nproc` -o main *.o
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -fuse-ld=lld  -o main *.o

Niektóre ograniczenia, które próbowałem złagodzić wybierając parametry testu:

  • w plikach C 100k obie metody czasami kończą się niepowodzeniem
  • GCC nie może skompilować funkcji z dodatkami 1M

Zauważyłem również 2x w kompilacji debugowania gem5: https://gem5.googlesource.com/public/gem5/+/fafe4e80b76e93e3d0d05797904c19928587f5b5

Podobne pytanie: /unix/545699/what-is-the-gold-linker

Testy porównawcze Phoronix

Phoronix przeprowadził pewne testy porównawcze w 2017 r. Dla niektórych rzeczywistych projektów, ale w przypadku projektów, które badali, zyski złota nie były tak znaczące: https://www.phoronix.com/scan.php?page=article&item=lld4-linux-tests&num = 2 ( archiwum ).

Znane niezgodności

Benchmarki LLD

Na https://lld.llvm.org/ podają czasy kompilacji dla kilku dobrze znanych projektów. z wynikami podobnymi do moich syntetycznych benchmarków. Niestety nie podano wersji projektu / konsolidatora. W ich wynikach:

  • złoto było około 3x / 4x szybsze niż LD
  • LLD było 3x / 4x szybsze niż złoto, a więc większe przyspieszenie niż w moim syntetycznym benchmarku

Komentują:

To jest porównanie czasu łącza na dwuprocesorowej, 20-rdzeniowej, 40-wątkowej maszynie Xeon E5-2680 2,80 GHz z dyskiem SSD. Uruchomiliśmy złoto i lld z obsługą wielowątkowości lub bez niej. Aby wyłączyć wielowątkowość, dodaliśmy -no-wątki do linii poleceń.

a wyniki wyglądają następująco:

Program      | Size     | GNU ld  | gold -j1 | gold    | lld -j1 |    lld
-------------|----------|---------|----------|---------|---------|-------
  ffmpeg dbg |   92 MiB |   1.72s |   1.16s  |   1.01s |   0.60s |  0.35s
  mysqld dbg |  154 MiB |   8.50s |   2.96s  |   2.68s |   1.06s |  0.68s
   clang dbg | 1.67 GiB | 104.03s |  34.18s  |  23.49s |  14.82s |  5.28s
chromium dbg | 1.14 GiB | 209.05s |  64.70s  |  60.82s |  27.60s | 16.70s

1
Mogę potwierdzić Twoje ustalenia, widzę podobne przyspieszenie w łączeniu moich projektów. Zobacz także testy porównawcze tutaj lld.llvm.org
ypnos


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.