Portál AbcLinuxu, 4. května 2025 22:25
A ano, v konecnem souctu je porad lepsi sdilene knihovny mit nez nemit ..., ale proste takovyhle oslavny zapis takhle po ranu ve me vyvolava vlnu sarkasmu.Můj blog se jmenuje Kacířské myšlenky. Takže je to snad jasné, ne (tím spíš, že jsem to zařadil do kategorie Rouhání největší). Sdílené knihovny samozřejmě trpí řadou neduhů. Ale některé výhody jsou tak drtivé, že je těžko něco převáží. Například ta snadná aktualizace - nedovedu si představit, že kvůli bugu např. v ZLIB budu muset přeinstalovat prakticky celý systém. Druhá drtivá výhoda je licenční, i když to ortodoxní zastánci free-SW neradi slyší.
Volani funkci z jine sdilene knihovny je opravdu pomalejsi nez z te same binarky. … Pokud se nepletu, PIC kod sam o sobe bezi asi o 10% (?) pomaleji.
Nedalo mi to a rozhodl jsem se, že si vyzkouším, jak to vlastně je. Napsal jsem si zmíněný prográmek, který vypisuje, "Hello, world!" tak, že na každé slovo si zavolá knihovní funkci. Jedna verze používá staticky linkovanou knihovnu, druhý dynamicky. Pro zájemce jsou ke stažení zdrojáky. Testováno na Athlon 64 3500+ (CFLAGS='-march=k8 -m64 -O3 -fomit-frame-pointer'
, LDFLAGS='-s'
) z běžícího KDE, měřeny jsou celkové časy příkazem time
. Prográmek vypisuje "hello, world!", tolikrát, kolik dostane jako parametr, byl opakovaně spouštěn for
cyklem bashe (kromě třetího testu, kdy byl spuštěn jen jedou). Pro solidnější test by to samozřejmě chtělo věrohodnější metodiku, ale i tak jsou výsledky docela zajímavé:
iterací parametr static dynamic 10^4 1 10.1±0.3 13.1±0.4 10^2 10^6 15.9±0.3 14.5±0.3 1 10^8 15.6±0.6 14.7±2.0
První řádek víceméně odpovídá tomu, že spuštění dynamicky linkované aplikace je o něco pomalejší, hodnota 0.3 ms ale IMHO není nějak závratně vysoká. Zajímavější jsou ale další dva řádky, které naznačují, že v tomto konkrétním případě je volání funkce z dynamicky linkované knihovny v průměru rychlejší. Je tu samozřejmě rozdíl v tom, že kód dynamické knihovny byl překládán s -fPIC
, ale to by podle vašich slov mělo také vést spíš k jeho zpomalení. Něco by mohl nasvědčovat i poměrně vysoký rozptyl hodnot u třetího testu (zvláště u dynamicky linkované verze) - jako by záleželo na tom, kam se relokace "trefí".
Mohl by někdo zkusit totéž na jiném systému?
měří opravdu prográmek rychlost práce procesoru a režii volání paměti, nebo spíš rychlost výstupu
Výstup šel v obou případech do /dev/null
a byl realizován stejnými funkcemi. Takže tady by rozdíl být neměl.
A možná nechápu metodiku, ale jak se počítá směrodatná odchylka z jednoho měření?
Měření nebylo jedno, u prvních dvou řádků byla tři, u posledního devět. Samozřejmě pro větší vypavídací hodnotu by bylo potřeba udělat měření více (a odfiltrovat zátěž generovanou zbytkem systému), ale jako námět k zamyšlení stačí i tohle.
Měření nebylo jedno, u prvních dvou řádků byla tři, u posledního devět. Samozřejmě pro větší vypavídací hodnotu by bylo potřeba udělat měření více (a odfiltrovat zátěž generovanou zbytkem systému), ale jako námět k zamyšlení stačí i tohle.
Aha, myslel jsem, že "iterací" je počet měření - už to chápu.
Výstup šel v obou případech do /dev/null a byl realizován stejnými funkcemi. Takže tady by rozdíl být neměl.
Nečekal jsem tady rozdíl, ale spíš zašumění všech ostatních rozdílů takovým způsobem, že budou nepozorovatelné :)
Tenhle výsledek mne každopádně trochu překvapil (můj tip by byl, že časy vyjdou nastejno v rámci chyby měření). Asi se na to večer podívám a zkusím to trochu vyprofilovat. Pořád bych si totiž vůbec nejsem jistý, co se vlastně měří. Časová úspora může být způsobena natahováním většího programu (i když se nejspíš cachuje) - jaké jsou velikosti programů?
Uvedený čas je předpokládám reálný čas? Možná by ostatní časy naznačily, kde se tráví většina doby.
Dynamická verze má 5192 B, statická 517736 B. Všechno je vzhledem k velkému počtu opakování samozřejmě z cache. Tabulky ukazují reálný čas, tedy přesněji elapsed time - zkusím to ještě předělat, aby se měřil skutečně jen vlastní user+system čas relevantních procesů, ale vzhledem k tomu, že ten systém jinak vykazuje zátěž procesoru pod jedno procento, nemělo by to na celkovém vyznění nic zásadního změnit.
Co se týká rozložení user/system, u prvního testu činí system něco kolem 6 sekund u statických, kolem 8 sekund u dynamických, u druhého testu je v obou případech lehce přes 0.1 :s a v posledním pod 0.1 s.
Ad 0. To sice měří, ale rychlost knihovní funkce fputs()
i driveru zařízení /dev/null
je v obou případech stejná. Jediný rozdíl je v tom, že v jednom měření je libwords
i libc
linkována staticky, ve druhém dynamicky.
Ad 1. Byl jste to vy, kdo dnes v 8:42 napsal: "Ja jsem tedy liny to merit, ale klidne bych se vsadil, ze v tomhle hello world pripade budou staticke knihovny vyhodnejsi."? Byl jste to vy, kdo dnes v 8:39 jednoznačně tvrdil, že je jasné, že volání funkce z dynamicky linkované knihovny bude pomalejší než ze staticky linkované knihovny? Pokud ano, míč je na vaší straně.
Ad 2. To samozřejmě ano, ale bude to tak významné, aby to vyvážilo zpomalení dané větším spustitelným souborem? Bude to tak významné, aby byl rozdíl pozorovatelný (tj. řekněme aspoň jedna sekunda)?
Ad 3. Časy jsou v sekundách, měření bylo vícekrát opakováno (proto jsou tam i odchylky). Ano, pro solidní závěry by bylo potřeba metodiku trochu vylepšit. Ale tak nevěrohodné, abyste nad tím bez přemýšlení mávnul rukou, to rozhodně není.
Tak jsem dopsal měřič, který provede n měření, každé spočívá v tom, že se n_iter-krát (první sloupec) spustí program s parametrem param a změří se celkový čas (user, system, součet). Výstupem jsou pak hodnoty průměru a střední odchylky pro user, system a total (total je user+system, takže to na rozdíl od původních výsledků neovlivňuje zbytek systému). Výsledky:
iterací verze param n u_avg u_dev s_avg s_dev t_avg t_dev 100000 ./hw-static 1 10 3.078 0.126 15.024 0.236 18.102 0.222 100000 ./hw-dynamic 1 10 13.942 0.307 29.101 0.535 43.043 0.441 100 ./hw-static 1000000 10 14.956 0.568 0.046 0.010 15.002 0.561 100 ./hw-dynamic 1000000 10 13.798 0.195 0.075 0.021 13.873 0.194 1 ./hw-static 100000000 10 16.300 0.136 0.032 0.016 16.332 0.125 1 ./hw-dynamic 100000000 10 14.993 2.051 0.048 0.010 15.041 2.054
Výsledky prvního testu, kdy převažuje režie spouštění programu, teď vypadají daleko přesvědčivěji pro statickou verzi, ale je to je dáno tím, že se zredukovala režie bashového cyklu, rozdíl zůstává přibližně na stejné hodnotě jako předtím (0.25 ms na jedno spuštění).
Výsledky druhého a třetího testu opět vycházejí lépe pro dynamickou verzi. Rovněž se opakuje obrovský rozptyl hodnot u dynamické verze ve třetím testu. Pohled na jednotlivé hodnoty pak ukazuje, že nejsou rozprostřeny nijak rovnoměrně, ale spíše "ode zdi ke zdi". To podporuje hypotézu, že záleží na tom, kam se při daném spuštění programu s knihovnou "trefíme".
Balíček je ke stažení tamtéž, co předtím. Pro testy na pomalejších procesorech doporučuji hodnoty trochu snížit.
Tak ještě jedny výsledky, tentokrát z druhého počítače:
iterací verze param n u_avg u_dev s_avg s_dev t_avg t_dev 100000 ./hw-static 1 10 3.486 0.077 15.017 0.135 18.503 0.174 100000 ./hw-dynamic 1 10 15.889 0.391 30.366 0.248 46.255 0.384 100 ./hw-static 1000000 10 14.701 0.085 0.086 0.017 14.787 0.083 100 ./hw-dynamic 1000000 10 15.708 0.094 0.088 0.021 15.796 0.091 1 ./hw-static 100000000 10 16.219 0.597 0.051 0.016 16.270 0.588 1 ./hw-dynamic 100000000 10 18.592 1.970 0.074 0.027 18.666 1.966
Tentokrát druhý a třetí test vycházejí opačně, jinak je vše velmi podobné. Přitom jediný rozdíl je v procesoru (první byl Athlon 64 3500+ se starším 130 nm jádrem, tohle je Athlon 64 3000+ s 90 nm jádrem Venice). Základní deska je stejná, operační systém také (SuSE Linux 10.1), jen na prvním počítači byl test spouštěn z běžícího KDE, zatímco na druhém z textového terminálu v runlevel 3.
iterací verze param n u_avg u_dev s_avg s_dev t_avg t_dev 100000 ./hw-static 1 10 2.601 0.073 12.295 0.119 14.896 0.090 100000 ./hw-dynamic 1 10 12.963 0.083 24.959 0.140 37.922 0.148 100 ./hw-static 1000000 10 20.427 0.030 0.098 0.022 20.525 0.023 100 ./hw-dynamic 1000000 10 25.862 0.335 0.104 0.027 25.966 0.321 1 ./hw-static 100000000 10 20.802 0.375 0.090 0.010 20.892 0.371 1 ./hw-dynamic 100000000 10 27.377 2.681 0.066 0.020 27.443 2.679Procesor, který si dal tu práci byl Athlon 64 3000+ (jádro Manchester, tj. 130 nm). Operační systém, pod kterým jsem to zkoušel, je Debian testing (i386) a také to bylo spuštěno z běžícího KDE.
Nehledě na to, že třeba v případě knihoven pro jazyk C++ je nutné použít také stejný kompilátor, stejné volby kompilátoru a kompatibilní verzi, jinak to sletí.Anebo si člověk musí naimplementovat vlastní komponentový model (už jsem s takovým programem dělal)
což v praxi dopadá často tak, že začnou chyby úplně jinde, protože nová verze knihovny jen málokdy pouze opraví chybu, ale udělá řadu jiných zásahůJenže za to nemůže knihovna, ale vývojář, který takhle prasí.
Nehledě na to, že třeba v případě knihoven pro jazyk C++ je nutné použít také stejný kompilátor, stejné volby kompilátoru a kompatibilní verzi, jinak to sletí.O tomhle jsem tu už kdysi psal a byl jsem za to některými lidmi i dost nevybíravých způsobem kritizován. Ale pořád to není dostatečný důvod, proč se sdílených knihoven bát.
dlopen()
. Hodí se to pro programy, kde hlavní program (ve spustitelném souboru) obsahuje jen základní funkcionalitu, kdežto všechno ostatní (třeba velice různorodé věci) je v knihovnách a málokdy se používají věci z různých knihoven současně.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.