Mam plik 1 TB. Chciałbym czytać od bajtu 12345678901 do bajtu 19876543212 i umieścić to na standardowym wyjściu na maszynie ze 100 MB pamięci RAM.
Mogę łatwo napisać skrypt Perla, który to robi. sysread dostarcza 700 MB / s (co jest w porządku), ale syswrite dostarcza tylko 30 MB / s. Chciałbym coś bardziej wydajnego, najlepiej coś, co jest instalowane w każdym systemie Unix i może dostarczać w ilości 1 GB / s.
Mój pierwszy pomysł to:
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
Ale to nie jest skuteczne.
Edytować:
Nie mam pojęcia, jak źle oceniłem syswrite. Zapewnia to 3,5 GB / s:
perl -e 'sysseek(STDIN,shift,0) || die; $left = shift; \
while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){ \
$left -= $read; syswrite(STDOUT,$buf);
}' 12345678901 $((19876543212-12345678901)) < bigfile
i unika yes | dd bs=1024k count=10 | wc
koszmaru.
bs=1M iflag=skip_bytes,count_bytes
Odpowiedzi:
Jest to powolne ze względu na mały rozmiar bloku. Korzystając z najnowszego GNU
dd
( coreutils v8.16 + ), najprostszym sposobem jest użycie opcjiskip_bytes
icount_bytes
:Aktualizacja
fullblock
opcja dodana powyżej zgodnie z odpowiedzią @Gilles . Na początku myślałem, że może to sugerowaćcount_bytes
, ale tak nie jest.Wspomniane problemy są potencjalnym problemem poniżej. Jeśli
dd
wywołania odczytu / zapisu zostaną z jakiegokolwiek powodu przerwane, dane zostaną utracone. W większości przypadków nie jest to prawdopodobne (szanse są nieco zmniejszone, ponieważ czytamy z pliku, a nie potoku).Korzystanie
dd
z opcji bezskip_bytes
icount_bytes
jest trudniejsze:Możesz także eksperymentować z różnymi rozmiarami bloków, ale zyski nie będą bardzo dramatyczne. Zobacz - Czy istnieje sposób na określenie optymalnej wartości parametru bs do dd?
źródło
bs
nie jest to czynnikiemskip
?skip
jest kilka bloków, a nie bajtów. Być może są zdezorientowani, ponieważskip_bytes
jest używany w pierwszym przykładzie rozumieniuskip
jest w bajtach tam?bs
jest4,096
, co oznacza, że nie możesz pominąć bardziej dokładnie4,096
bajtówdd
z pierwszym i ostatnim użyciembs=1
w celu skopiowania danych, które nie zaczynają się ani nie kończą przy wyrównaniu bloku.bs=1
każedd
czytać i pisać jeden bajt na raz. Na każde połączenieread
iwrite
połączenie przypada narzut , co powoduje, że jest to wolne. Użyj większego rozmiaru bloku, aby uzyskać przyzwoitą wydajność.Kiedy kopiujesz cały plik, przynajmniej pod Linuksem, znalazłem to
cp
icat
jest szybszy niżdd
, nawet jeśli określisz duży rozmiar bloku.Aby skopiować tylko część pliku, możesz dokonać potoku
tail
dohead
. Wymaga to GNU coreutils lub innej implementacji, która musihead -c
skopiować określoną liczbę bajtów (tail -c
jest w POSIX, alehead -c
nie ma). Szybki test porównawczy w systemie Linux pokazuje, że jest to wolniejsze niżdd
, prawdopodobnie z powodu potoku.Problem
dd
polega na tym , że nie jest on wiarygodny: może kopiować częściowe dane . O ile mi wiadomo,dd
jest bezpieczny podczas odczytu i zapisu do zwykłego pliku - patrz Kiedy dd nadaje się do kopiowania danych? (lub, gdy są czytane () i write () częściowe) - ale tylko tak długo, dopóki nie zostanie przerwany przez sygnał . W jądrach GNU możesz używaćfullblock
flagi, ale nie jest to przenośne.Innym problemem
dd
jest to, że znalezienie liczby bloków, która działa, może być trudne, ponieważ zarówno liczba pominiętych bajtów, jak i liczba przesłanych bajtów musi być wielokrotnością wielkości bloku. Możesz użyć wielu wywołań dodd
: jednego, aby skopiować pierwszy blok częściowy, jednego, aby skopiować większość wyrównanych bloków i jednego, aby skopiować ostatni blok częściowy - patrz odpowiedź Graeme na fragment kodu powłoki. Ale nie zapominaj, że kiedy uruchamiasz skrypt, chyba że używaszfullblock
flagi, musisz się modlić,dd
aby skopiować wszystkie dane.dd
zwraca wartość niezerową, jeśli kopia jest częściowa, więc łatwo jest wykryć błąd, ale nie ma praktycznego sposobu na jego naprawienie.POSIX nie ma nic lepszego do zaoferowania na poziomie powłoki. Radzę napisać mały program C specjalnego przeznaczenia (w zależności od tego, co dokładnie zaimplementujesz, możesz go nazwać
dd_done_right
lubtail_head
lubmini-busybox
).źródło
yes | dd bs=1024k count=10 | wc
wcześniej nie znałem problemu. Paskudny.Z
dd
:Alternatywnie z
losetup
:A potem
dd
,cat
... urządzenie loop.źródło
Oto jak możesz to zrobić:
To wszystko, co jest naprawdę konieczne - nie wymaga wiele więcej. Po pierwsze
dd count=0 skip=1 bs=$block_size1
,lseek()
nad regularnym wprowadzaniem plików praktycznie natychmiast. Nie ma szans na pominięcie danych lub jakiekolwiek inne nieprawdziwe informacje na ten temat, możesz po prostu szukać bezpośrednio do pożądanej pozycji początkowej. Ponieważ deskryptor pliku jest własnością powłoki, a plikidd
po prostu dziedziczą ją, wpłyną one na jej pozycję kursora, więc możesz to zrobić krok po kroku. To naprawdę jest bardzo proste - i nie ma standardowego narzędzia, które lepiej odpowiadałoby temu zadaniu niżdd
.Wykorzystuje rozmiar bloku 64k, co często jest idealne. W przeciwieństwie do powszechnego przekonania, większe rozmiary bloków nie przyspieszają
dd
pracy. Z drugiej strony małe bufory też nie są dobre.dd
musi zsynchronizować swój czas w wywołaniach systemowych, aby nie musiał czekać na kopiowanie danych do pamięci i ponownie, ale także, aby nie musiał czekać na wywołania systemowe. Więc chcesz, aby zajęło to wystarczająco dużo czasu, aby następnyread()
nie musiał czekać na ostatni, ale nie tak bardzo, że buforujesz w większych rozmiarach niż to konieczne.Pierwszy
dd
przeskakuje do pozycji początkowej. To zajmuje zero czasu. Możesz wywołać dowolny inny program, który lubisz w tym momencie, aby odczytać jego standardowe wejście, a program zacznie czytać bezpośrednio z wybranym przesunięciem bajtu. Dzwoniędd
do innego, żeby odczytać((interval / blocksize) -1)
bloki liczników na standardowe wyjście.Ostatnią rzeczą, która jest konieczna, jest skopiowanie modułu (jeśli istnieje) z poprzedniej operacji dzielenia. I to jest to.
Nawiasem mówiąc, nie wierz w to, kiedy ludzie przedstawiają fakty na twarzy bez dowodów. Tak, możliwe jest
dd
wykonanie krótkiego odczytu (chociaż takie rzeczy nie są możliwe przy odczycie ze zdrowego urządzenia blokowego - stąd nazwa) . Takie rzeczy są możliwe tylko wtedy, gdy nie buforujesz poprawniedd
strumienia odczytanego z urządzenia innego niż urządzenie blokowe. Na przykład:W obu przypadkach
dd
skopiuj wszystkie dane. W pierwszym przypadku jest to możliwe (choć mało prawdopodobne, zcat
) , że niektóre z bloków wyjściowych, któredd
kopiuje OUT bit równy „$ num” Bajty ponieważdd
jest spec''d tylko do bufora w ogóle nic, gdy bufor jest wyraźnie wymagane na jej Command linia.bs=
reprezentuje maksymalny rozmiar bloku, ponieważ celemdd
jest we / wy w czasie rzeczywistym.W drugim przykładzie jawnie określam wyjściowy rozmiar bloku i
dd
odczyty buforów, aż możliwe będzie pełne zapisanie . Nie ma to wpływu nacount=
to, co jest oparte na blokach wejściowych, ale do tego potrzebujesz tylko innegodd
. Wszelkie dezinformacje podane w inny sposób powinny zostać zignorowane.źródło