Мое приложение сканирует часть файловой системы, и мои пользователи сообщили, что это было очень медленно, когда они сканировали сетевой диск. Тестируя мой код, я определил узкое место: методы File.isFile()
, File.isDirectory()
, и File.isHidden()
, которые все звонят fs.getBooleanAttributes(File f)
. Этот метод, кажется, является очень медленным на сетевых дисках Windows. Как я могу улучшить производительность? Я могу постараться не называть этот метод в некотором роде?
Как вы составляете этот список файлов? Если вы не показываете все файлы в системе одновременно, у вас должны быть некоторые параметры ...
Возможно, если вы покажете код, вы являетесь используя для построения списка, мы могли бы найти некоторые другие области улучшения. (Почему вы не можете просто вывести тип на основе метода, используемого для сбора информации? Если вы вызываете такой метод, как GetFiles (), разве вы еще не знаете, что все возвращаемое - это файл?)
Защитный код часто вызывает методы isXYZ ()
, и это обычно хорошая практика. Однако, как вы обнаружили, иногда производительность низкая.
Альтернативный подход состоит в том, чтобы предположить, что файл является файлом, он существует, он видим, доступен для чтения и т.д., и просто попробуйте прочитать его. Если это не те вещи, вы получите исключение, которое сможете перехватить, а затем выполните проверки, чтобы точно выяснить, что пошло не так. Таким образом, вы оптимизируете общий случай (то есть все в порядке) и выполняете медленные операции только тогда, когда что-то идет не так.
Я столкнулся с точно такой же проблемой
Решение для нашего случая было довольно простым: поскольку наша структура каталогов соответствовала стандарту (там, где не было каталога, в котором был бы символ '.' name), я просто следовал стандарту и применил очень простую эвристику: «в нашем случае каталоги не имеют символа '.' символ в его имени ". Эта простая эвристика резко сократила количество вызовов нашему приложению функции isDirectory () класса java.io.File.
Возможно, это ваш случай. Возможно, по структуре каталогов вы сможете узнать, является ли файл каталогом, только по соглашению об именах.
На случай, если вы еще не пробовали, вызвать getBooleanAttributes самостоятельно и выполнить необходимое маскирование будет значительно быстрее, если вы выполняете несколько проверок одного и того же файла. Хотя это не идеальное решение (и то, которое начинает подталкивать ваш код к привязке к платформе), оно может повысить производительность в 3 или 4 раза. Это довольно значительный прирост производительности, хотя и не так быстро, как должно быть.
Функциональность JDK7 java.nio.file.Path должна немного помочь в подобных делах.
Наконец, если у вас есть какой-либо контроль над средой конечного пользователя, предложите своим пользователям настроить свои антивирусное программное обеспечение, чтобы не сканировать сетевые диски. Многие крупные антивирусные решения (не совсем понимающие, что именно они решают) по умолчанию включают эту функцию. Я не
Вот пример кода до и после использования listFiles
и использования isDirectory
для обхода дерева каталогов (мой код использует общий обратный вызов для фактического выполнения что-то с каждым каталогом и файлом; если бы я кодировал C #, это был бы делегат).
Как видите, подход listFiles
на самом деле более компактен и понятен, а также немного быстрее на локальный диск (950 мс против 1000 мс) и диск LAN (26 секунд против 28 секунд), оба для 23 тысяч файлов.
Вполне возможно, что для удаленного подключенного диска ускорение может быть значительным, но я могу ' t проверьте это с работы. Как ни странно, скорость передачи через Windows RAS VPN на сетевой диск по-прежнему составляет всего около 10%.
Новый код
static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
dir=dir.getAbsoluteFile();
return _processDirectory(dir.getParentFile(),dir,new Callback.WithParams(cbk,2),sel);
}
static private int _processDirectory(File par, File fil, Callback.WithParams cbk, FileSelector sel) {
File[] ents=(sel==null ? fil.listFiles() : fil.listFiles(sel)); // listFiles returns null if fil is not a directory
int cnt=1;
if(ents!=null) {
cbk.invoke(fil,null);
for(int xa=0; xa<ents.length; xa++) { cnt+=_processDirectory(fil,ents[xa],cbk,sel); }
}
else {
cbk.invoke(par,fil); // par can never be null
}
return cnt;
}
Старый код
static public int oldProcessDirectory(File dir, Callback cbk, FileSelector sel) {
dir=dir.getAbsoluteFile();
return _processDirectory(dir,new Callback.WithParams(cbk,2),sel);
}
static private int _processDirectory(File dir, Callback.WithParams cbk, FileSelector sel) {
File[] ents=(sel==null ? dir.listFiles() : dir.listFiles(sel));
int cnt=1;
cbk.invoke(dir,null);
if(ents!=null) {
for(int xa=0; xa<ents.length; xa++) {
File ent=ents[xa];
if(!ent.isDirectory()) {
cbk.invoke(dir,ent);
ents[xa]=null;
cnt++;
}
}
for(int xa=0; xa<ents.length; xa++) {
File ent=ents[xa];
if(ent!=null) {
cnt+=_processDirectory(ent,cbk,sel);
}
}
}
return cnt;
}