LinuxForum.Fans
Тема

Самый быстрый способ выборки случайного файла из директории

Автор: valet 52 сообщений 10606 просмотров
#298371

Задача такая - имеется один скрипт, который постоянно складывает файлы в каталог kat1. Второй скрипт должен постоянно брать случайный файл из kat1, обрабатывать его (sed'ом) и скидывать обработанную версию файла в другой каталог kat2.

Возник вопрос: какая конструкция может быстро получать имя случайного файла в каталоге.
find здесь наверно не пойдет, потому что файлы постоянно добавляются новые файлы...

#298374
"valet" написал:
Возник вопрос: какая конструкция может быстро получать имя случайного файла в каталоге.
text
#!/bin/sh
array=($(ls))
len=${#array[*]}
rand=$[ $RANDOM % $len ]
echo ${array[$rand]}
#298376

xxblx, ого. Спасибо конечно за вариант, но как-то слишком в лоб мне кажется. Мне кажется это будет тормозить весь процесс...
Может тогда уже лучше что-то типа:

text
find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

здесь
find . -maxdepth 1 -mmin -10 - найдет все файлы в текущем каталоге, измененные за последние 10 мин.
shuf -n1 - выбор случайной строки-файла
cut -f2 -d"/" - вырезаем имя, так как find возвращает файлы в формате ./file

И то я за find переживаю, даст ли ускорение в плане поиска ключ -mmin? Может есть еще что быстрее?

Или тогда уже через ls, то тогда уже

text
ls|shuf -n1

Хоть не надо строить никакой массив.

Вот что быстрее будет ls или find с ограничением по ключу -mmin?

#298380
"valet" написал:
но как-то слишком в лоб мне кажется.

вопрос был поставлен так: "какая конструкция может быстро получать имя случайного файла в каталоге." - на него я и дал ответ в силу своих знаний (а они у меня невелики). а дальше уж сам смотри подходит он тебе или нет.

"valet" написал:
Вот что быстрее будет ls или find с ограничением по ключу -mmin?

сделай сначала

text
time find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

а потом

text
time ls|shuf -n1

и сравни результаты ;)

#298389
"xxblx" написал:
вопрос был поставлен так: "какая конструкция может быстро получать имя случайного файла в каталоге."

Да. Возможно мне надо было четче уточнить, что ключевое здесь "быстро", так как методов и так понятно что много. В первую очередь я ожидал что может даже есть какая-то утилитка малоизвестная которая выдает имя случайного файла в каталоге и работает скажем как-то системными вызовами из-за чего очень быстро...

"xxblx" написал:
на него я и дал ответ
Да, я же поблагодарил. Еще раз спасибо ))

По поводу быстродействия - find намного быстрее таки:
text
valet@valet-comp:/files$ ls |wc -l
1138276
valet@valet-comp:/files$ time find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

real 0m1.781s user 0m0.348s sys 0m1.432s valet@valet-comp:/files$ time ls|shuf -n1 temp-1358240990.html

real 0m6.643s user 0m6.136s sys 0m0.572s

#298419
valet написал:

По поводу быстродействия - find намного быстрее таки:

Я бы по два-три раза подряд повторил тест на скорость, тк при первом поиске наверняка читалось с диска, а при втором уже из кэша.

#298437

А если Python попробовать?

text

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from os import listdir from random import choice print choice(listdir('.'))

#298971

"-=Serj=-" написал:
А если Python попробовать?
Это фантастика, на каталоге в миллион файлов Python показывает скорость в 5 раз выше find'а. Всунул ваш срипт в testperl:
text
 valet@valet-comp:~/home$ ls files|wc -l
1138276
 valet@valet-comp:~/home$ time find files -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

real 0m3.428s user 0m0.348s sys 0m2.716s valet@valet-comp:~/home$ time ./testperl temp-1358312639.html

real 0m0.737s user 0m0.156s sys 0m0.556s valet@valet-comp:~/home$

В чем секрет, откуда такая скорость у питона? Он что как-то принципиально по-другому работает с файловой системой или диском?

#298982

Скорее всего, Python скрипт не быстрее find, но быстрее цепочки пайпов.
ЗЫ. И да, тест повторяли несколько раз для чистоты эксперимента?

#299083

"-=Serj=-" написал:
ЗЫ. И да, тест повторяли несколько раз для чистоты эксперимента?
Да, конечно, время менялось, но менялось там и там, и каждый раз питон был в ~6раз быстрее, чем конструкция с find.

"-=Serj=-" написал:
Скорее всего, Python скрипт не быстрее find, но быстрее цепочки пайпов.
Может быть, но почему тогда он быстрее чем
text
ls|shuf -n1
#299113
"valet" написал:
Может быть, но почему тогда он быстрее чем ls|shuf -n1

в порядке затрат времени:
1) ls сортирует по алфавиту
2) тормоза в трубе
3) накладные расходы на exec() и подгрузку либ

"valet" написал:
valet@valet-comp:/files$ time find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

правильно было бы измерять так: time bash -c 'find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"'
иначе относительно правильным будет только real-время
ЗЫ: если большинство файлов относительно новые, то mmin будет сильно тормозить...

#299280

"Fat-Zer" написал:
иначе относительно правильным будет только real-время
А я на него и смотрел ))

"Fat-Zer" написал:
ЗЫ: если большинство файлов относительно новые, то mmin будет сильно тормозить...
не заметил подобного.
#299335
"valet" написал:
не заметил подобного.

в 2,5 раза разница:

text
alexander@goblin /tmp/tst $ time find /tmp/tst/ -maxdepth 1 >/dev/null

real 0m0.393s user 0m0.219s sys 0m0.172s alexander@goblin /tmp/tst $ time find /tmp/tst/ -maxdepth 1 -mmin 1000 >/dev/null

real 0m1.070s user 0m0.184s sys 0m0.882s alexander@goblin /tmp/tst $ find /tmp/tst/ -maxdepth 1 |wc -l 940212

и это в tmpfs всё... на железках разница будет несколько меньше, но всё равно существенна:

text
alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches
3
alexander@goblin ~/tst $ time find . -maxdepth 1 >/dev/null

real 0m39.109s user 0m0.235s sys 0m0.456s alexander@goblin ~/tst $ time find . -maxdepth 1 >/dev/null

real 0m0.495s user 0m0.213s sys 0m0.280s alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches 3 alexander@goblin ~/tst $ time find . -maxdepth 1 -mmin 1000 >/dev/null

real 0m56.808s user 0m0.373s sys 0m4.153s alexander@goblin ~/tst $ time find . -maxdepth 1 -mmin 1000 >/dev/null

real 0m1.100s user 0m0.194s sys 0m0.902s alexander@goblin ~/tst $ find . -maxdepth 1 |wc -l 940213

кстати без кеша разница с питоном минимальная, а с кешами меньше, чем в 1,5 раза...

text
alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches
3
alexander@goblin ~/tst $ time bash -c "find . -maxdepth 1 | shuf -n1"
./692426270

real 0m30.249s user 0m0.274s sys 0m0.519s alexander@goblin ~/tst $ time bash -c "find . -maxdepth 1 | shuf -n1" ./30414668106

real 0m0.521s user 0m0.227s sys 0m0.304s alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches 3 alexander@goblin ~/tst $ time ./my.py 15321712054

real 0m29.831s user 0m0.088s sys 0m0.477s alexander@goblin ~/tst $ time ./my.py 19621382664

real 0m0.318s user 0m0.087s sys 0m0.229s

#299337

То есть делаем выводы, что как не крути - Питон быстрее ))
Это кстати все равно интересно ))

#299442
"valet" написал:
То есть делаем выводы, что как не крути - Питон быстрее ))
Это кстати все равно интересно ))

и на перловке наверняка быстрее получится, а на си ещё быстрее...
но если весь код на баше, то к таким ухищрениям надо прибегать только если скорость неудовлетворительно и доказано, что узкое место именно здесь... перфекционизм совершенно излишен...
кстати ls -f показывает себя лучше find'а...

#299635

"Fat-Zer" написал:
и на перловке наверняка быстрее получится
еще бы примерчик был бы в самый раз, а вдруг :)

"Fat-Zer" написал:
но если весь код на баше, то к таким ухищрениям надо прибегать только если скорость неудовлетворительно и доказано, что узкое место именно здесь
да, именно узкое место вот такая вот выборка случайного файла, дальше идет обработка этого случайного файла sed'ом - и это уже быстро, так как работа с одним конкретным файлом (ну а даже если не быстро - это уже никак не ускорить). Почему надо выбирать именно случайный - дело в том, что каталог постоянно в онлайне пополняется новыми файлами, то есть пройтись по определенному списку никак не получится, приходится постоянно получать с каталога случайный.

"Fat-Zer" написал:
кстати ls -f показывает себя лучше find'а...
действительно, протестил, так и есть. Но Python еще быстрее - вот в чем фишка :)

Еще раз все тесты 2 раза на каталоге в более 1 млн файлов:
Показать
valet@valet-comp:$:~/data/files/temp$ ls |wc -l
1138276
valet@valet-comp:$:~/data/files/temp$ time ls|shuf -n1
temp-1358284308.html

real 0m6.337s
user 0m5.840s
sys 0m0.556s
valet@valet-comp:$:~/data/files/temp$ time find . -maxdepth 1 -mindepth 1 -type f -mmin -100000|shuf -n1|cut -f2 -d"/"
temp-1358279553.html

real 0m1.954s
user 0m0.568s
sys 0m1.444s
valet@valet-comp:$:~/data/files/temp$ time ls -f|shuf -n1
temp-1358338858.html

real 0m0.887s
user 0m0.488s
sys 0m0.448s
valet@valet-comp:$:~/data/files/temp$ time ../../rand_file_python
temp-1358293655.html

real 0m0.555s
user 0m0.156s
sys 0m0.400s
valet@valet-comp:$:~/data/files/temp$ time ls|shuf -n1
temp-1358275701.html

real 0m7.476s
user 0m6.264s
sys 0m0.560s
valet@valet-comp:$:~/data/files/temp$ time find . -maxdepth 1 -mindepth 1 -type f -mmin -100000|shuf -n1|cut -f2 -d"/"
temp-1358414618.html

real 0m1.906s
user 0m0.516s
sys 0m1.444s
valet@valet-comp:$:~/data/files/temp$ time ls -f|shuf -n1
temp-1358290731.html

real 0m0.878s
user 0m0.428s
sys 0m0.496s
valet@valet-comp:$:~/data/files/temp$ time ../../rand_file_python
temp-1358248393.html

real 0m0.568s
user 0m0.128s
sys 0m0.440s

Кстати как видим я специально для find взял mmin большое, чтобы в критерий попадали заведомо все файлы. Все равно find быстр, намного быстрее обычного ls, правда ls -f уже быстрее find, но python несомненный лидер, как видим, он обыгрывает по скорости обычный ls чуть не в 15 раз и быстрее ls -f в 1.5 раза. Для меня кстати до сих пор загадка почему такое происходит, очевидно питон как-то по другому делает системные вызовы к ФС или диску - не могу точно знать, не силен в таких деталях.
Но для моей задачи этот питон более чем, полезен, поэтому большое спасибо -=Serj=- за наводку.

#299649

Чтобы не создавать новую тему, хотелось бы еще кое что спросить...

Вот мой скрипт, который обрабатывает файлы (с помощью sed-скрипта script) с большого каталога html_in (в него постоянно другим скриптом докладываются файлы) и складываются в обработанном виде в другой каталог html_out, удаляя в исходном этот файл:

text
#!/bin/bash

cd html_in/

while [ `../rand_file_python|wc -l` -gt 0 ] do file=`../rand_file_python` ../script $file > ../html_out/$file rm -f $file done

cd ../

Выборка случайного файла из каталога происходит питоновским скриптом, предложенном в этой теме rand_file_python:

text
#!/usr/bin/env python
#-*- coding: utf-8 -*-

from os import listdir from random import choice print choice(listdir('.'))

Но мне в моем скрипте не нравится то, что приходится 2 раза вызывать выборку случайного файла: один раз для while для проверки условия продолжения обработки цикла, второй раз уже непосредственно для получения случайного файла.
Кто подскажет как это правильно обойти? Думаю можно как-то обойтись одним вызовом с присовением в переменную, но как это правильно сделать в условии обхода цикла?

П.С. Гугление по сабжу "bash while переменная в условии" ничего мне не подсказало, видать не правильно гуглю ))

#299659

Использовать что-то, вроде

text

while True
do
file = `../rand_file_python`
if file не существует
    then break
script
done
#299705

"-=Serj=-" написал:
if file не существует
Это можно, но тогда лишней будет конструкция if file не существует, хотя может это уже попроще будет - все таки не выборка случайного файла.
Попробую пока так...
#299711
"valet" написал:
но тогда лишней будет конструкция if file не существует

так она уже итак присутствует:

"valet" написал:
`../rand_file_python|wc -l` -gt 0
Войдите или зарегистрируйтесь, чтобы ответить.