Alternatívy pre cp pre CLI

Včera | 10:13 | Richard | Richard

Známy web linuxlinks.com upozornil na program v Rust-e, ktorý nahrádza známy rm.  Napadlo ma, či jeho autor neurobil aj verziu pre cp. A veru áno.

cp, cpz a iné

Program cp je najklasickejší a najzákladnejší program určený na kopírovanie súborov a adresárov, naprogramovaný v jazyku C. Je vskutku základný, nevie obnoviť prerušené kopírovanie, nekontroluje kontrolné súčty, nemá štatistiky priebehu ani odhad času (a to chýba najmä pri kopírovaní veľkých súborov), nemá synchronizáciu (teda vie len prepísať všetko, nevie porovnať zdroj a cieľ), vždy skopíruje celý obsah súborov.

Takže skutočný základ. A vo väčšine prípadov plne postačujúci.

Komu by to nepostačovalo, tak ten môže siahnuť po iných programoch. Ja zvyčajne používam rsync v aliase alias rcp rsync -hPur $* (pozor, je to alias pre Emacs Eshell, ktorý sa definuje trošku odlišne). To mi pridá informácie o priebehu a tiež robí prírastkové kopírovanie.

Ale tých programov je viac, tak si ich len spomenieme. A spravíme si test, ako rýchlo dokážu pracovať.

Testovací adresár

V eshelle Emacsu som si spustil v adresári test_dir jednoriadkový príkaz, ktorý mi vygeneroval 300 súborov s náhodou veľkosťou od 100 kB po 50 MB. Celková veľkosť obsahu adresára je 7488,48 MB, takže cca 7,4 GB.

Adresár som kopíroval na SSD disku, z jednej partície na druhú.

A ešte ten one-liner:

(dotimes (i 300) (let ((size (+ 100 (* (random 50000) 1024)))) (with-temp-buffer (insert (make-string size ?\0)) (write-file (format "file_%d" i)))))

 

A teraz konkrétne testy:

Pôvodným zámerom bolo spustiť takýto príkaz:
for f in 1 2 3 4 5 { time cp -r test_dir /media/extended/$f }

Ale nefungovalo to ako som chcel, lebo to počítalo celkové kopírovanie počas piatich dieľčich (čo je z hľadiska zistenia priemeru jedného v poriadku, ale zaujímalo ma i to, či sa budú jednotlivé kopírovania líšiť, a veru sa aj!).
Ono v Eshell-i nie je time obyčajným externým programom, ale interne rozpoznávanou štruktúrou, ktorá sa aplikuje na celý nasledujúci job a keď je v { … } bloku, tak sa zmeria práve vykonanie toho jedného a nie každá iterácia zvlášť.

 

Tak som to obišiel takto:

Príkaz kopírovania som spúšťal 5×, pomocou príkazu:
bash -lc 'for f in {1..5}; do time cp -r test_dir /media/extended/$f; done'

(kde cp -r som nahrádzal vždy iným príkazom).

Fanúšikovia Emacsu mi hádam odpustia, že som ako úkrok bokom použil spustenie procesu bash-u. ☺

Počas kopírovania som nerobil s počítačom nič iné, bola nastavená fixná frekvencia CPU (všetky 4 jadrá na 1,6 GHz).

 

cp

S použitím cp -r som dosiahol tieto výsledky:

kopírovanie č.čas [s]
145,599
239,172
353,232
439,994
541,903
43,980

cpz

Na githube autora programu rmz som našiel tento program cpz (skratka od cp zippy). Je naprogramovaný v Rust-e a motiváciou autora bolo spraviť rýchlejšiu alternatívu v cp. Jeho použitie je identické ako v prípade cp.

S použitím cpz som dosiahol tieto výsledky (cpz nepotrebuje parameter pre určenie rekurzívneho kopírovania):

kopírovanie č.čas [s]
143,907
234,271
333,244
442,185
534,550
37,631

gcp

Z dokumentácie vyberáme:
gcp je kopírovací program na kopírovanie súborov, voľne inšpirovaný príkazom cp, ale s funkciami, ako sú:

S použitím gcp -r som dosiahol tieto výsledky:

kopírovanie č.čas [s]
1180,653
2191,212
3142,631
4194,301
5123,246
166,409

Samozrejme, nechal som zapnuté zobrazovanie priebehu, pretože je to hlavný dôvod použia.

Len dodám, že gcp je v štandardných repozitároch distribúcií.

Screenshot, aby čitateľ videl, ako vyzerá progress-bar pri kopírovaní: 

rsync

Rsync je všeobecne známy nástroj na synchronizáciu a kopírovanie súborov medzi dvomi miestami, lokálne alebo naprieč sieťou (napr. cez ssh). Na rozdiel od cp, rsync dokáže efektívne prenášať iba zmenené časti súborov. Je na ňom postavených veľa utilít ako grsync, luckybackup či timeshift.

Hlavné výhody:

S použitím rsync -hPur som dosiahol tieto výsledky:

kopírovanie č.čas [s]
180,238
2105,415
362,813
4103,318
561,158
82,564

Opäť - nechal som zapnuté zobrazovanie priebehu, lebo to je tiež významná výhoda oproti cp.

tar

A ešte jedna exotika: použitie tar, ktorý vytvorí a potom rozbalí archív medzi dvomi adresármi.

Tu som ale ručne spúštal tento príkaz 5×:

tar cf - test_dir | tar xf - -C /media/extended/

kopírovanie č.čas [s]
146,844
231,866
330,419
445,166
535,252
37,909

Záver

Časy pri kopírovaní sú ovplyvnené množstvom faktorov – často tak veľmi, že rovnaký príkaz pri viacerých spusteniach dáva rozdielne výsledky.

Dôvodom môže byť rozdielne zaťaženie hardvéru – aj keď som počítač počas testu nepoužíval, len sa na neho pozeral (žeby kvantový efekt pozorovateľa 😏?), ale zase nejaký iný proces v OS mohol dačo iné robiť. Potom je tu nejaká cache, na ktorou nemám (alebo o tom neviem) kontrolu. A možno aj iné veci…

Takže výsledky v jednej tabuľke (a org módom generovaným grafom 😏):

program⌀ čas [s]ASCII graf
  (menej je lepšie)
cp43,980WWW
cpz37,631WWl
gcp166,409WWWWWWWWWWWh
rsync82,564WWWWWV
tar37,909WWl

A aké je ponaučenie?

Grafické „parádičky“ sú výhodné, lebo má človek informáciu o priebehu. gcp, ktorého progress-bar je postavený na python knižnici, pokuľháva vo výkone, zvlášť v našom prípade, keď sa emuluje zobrazenie bash procesu do eshell terminálu. rsync je pomalší samozrejme aj preto, že musí každú položku pred kopírovaním overiť.

tar príjemne prekvapil, ale vzhľadom na syntax je použitie otázne, hoci by sa to dalo zaobaliť do funkcie. Oproti cp alebo rsync robí menej volaní jadra, vie použiť veľké bloky a sústredí sa čisto na sekvenčné čítanie/zápis, bez všetkého navyše, čo robí rsync, ktorý pri stovkách súborov asi má dopad na výkon.

cpz a Rust priniesli trochu rýchlosti, ale v našom prípade to bolo úplne irelevantné. Ale zase je to „hype“ jazyk, no…

Takže pre mňa je to jasné: 90% obslúžim pomocou cp, zvyšok, najmä veľké súbory alebo pri práci s pomalými diskami, pomocu rsync.

Prílohy