Rekurencyjne zakładanie katalogów

Czasami przychodzi potrzeba założenia dużej ilości zagnieżdżonych katalogów, w celu przygotowania jakiejś struktury do przechowywania danych, można to zrobić na kilka sposobów, najlepiej w taki który jest dla nas najwygodniejszy. W przypadku kiedy takich struktur zakładamy wiele, albo są bardzo zagnieżdżone, warto pomyśleć również nad optymalizacją.

Jak myślicie, czy da się przyśpieszyć zakładanie katalogów poprzez użycie mkdir -p?

Posłużę się pewnym przykładem:

mamy 3 zagnieżdżone pętle i mkdir -p, skrypt tworzy taką strukturę katalogów:

0/0/0,
0/0/1,
... ,
f/f/e,
f/f/f,

założenie 4096 katalogów na moim sprzęcie trwa kilkanaście sekund, po przyjrzeniu się bliżej, z użyciem strace ;-)

mkdir("a", 0755) = 0
open("a", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_NOFOLLOW) = 3
fchdir(3) = 0
close(3) = 0
mkdir("a", 0755) = 0
open("a", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_NOFOLLOW) = 3
fchdir(3) = 0
close(3) = 0
mkdir("a/", 0755) = 0

widać, że podczas zakładania katalogów, mkdir -p za każdym razem wywołuje funkcje open(), fchdir() i dopiero mkdir(), co można analogicznie przełożyć na:

# mkdir a
# cd a
# mkdir a
# cd a
# mkdir a

znacznie szybciej byłoby to zrobić następująco:

# mkdir a
# mkdir a/a
# mkdir a/a/a

dodatkowo, najbardziej kosztownym czynnikiem jest to, że przy każdej iteracji pętli, mkdir musi zostać ponownie uruchomiony (dziękuję BartOwl za zwrócenie uwagi), czyli pozostają 2 możliwości, zmodyfikować powyższą pętle w bashu lub iść na łatwiznę i użyć Perla ;-)

który zachowuję się następująco:

mkdir("3", 0777) = 0
mkdir("3/0", 0777) = 0
mkdir("3/0/0", 0777) = 0
stat64("3/0/1", 0x81530c8) = -1 ENOENT (No such file or directory)
stat64("3/0", {st_mode=S_IFDIR|0755, st_size=72, ...}) = 0
mkdir("3/0/1", 0777) = 0
stat64("3/0/2", 0x81530c8) = -1 ENOENT (No such file or directory)
stat64("3/0", {st_mode=S_IFDIR|0755, st_size=96, ...}) = 0
mkdir("3/0/2", 0777) = 0

dla tej ilości katalogów operacja wykonuje się ewidentnie dużo szybciej, poniżej czasy wykonania dla skryptu bash'owego:

root@getpost:/home/jamzed/test# time ./createdir.sh
real 0m12.845s
user 0m7.133s
sys 0m5.692s

oraz dla skryptu napisanego w Perlu:

root@getpost:/home/jamzed/test# time ./createdir.pl
real 0m0.558s
user 0m0.332s
sys 0m0.226s

Wiem, że może być trudno sobie wyobrazić potrzebę zakładania takich struktur, ale tym wpisem chciałem pokazać, że czasami jedna bardzo mała zmiana może spowodować wzrost wydajności i spadek czasu wykonywania kilkadziesiąt razy. Poza tym, zawsze warto wiedzieć jak dokładnie działa jakieś narzędzie i ile można z niego jeszcze wycisnąć ;-)

A Wy które elementy systemów optymalizujecie najczęściej?