2009-12-18 3 views
8

Мое приложение сканирует часть файловой системы, и мои пользователи сообщили, что было очень медленно при сканировании сетевого диска. Испытывая мой код, я определил узкое место: методы File.isFile(), File.isDirectory() и File.isHidden(), которые все вызывают fs.getBooleanAttributes(File f). Этот метод работает очень медленно на сетевых дисках Windows. Как повысить производительность? Можно ли каким-либо образом вызвать этот метод?Ускорение доступа к файловой системе?

ответ

6

Как вы строите этот список файлов? Если вы не показываете каждый файл в системе одновременно, у вас должны быть некоторые параметры ...

  1. Обработайте эту информацию только в том случае, если пользователь запрашивает ее. например Они нажимают на папку «Windows», после чего вы можете обрабатывать файлы в Windows.
  2. Обработайте эту информацию в фоновом режиме, создавая иллюзию лучшего времени отклика.

Возможно, если вы покажете код, который используете для создания списка, мы могли бы найти некоторые другие области улучшения. (Почему вы не можете просто вывести тип на основе метода, используемого для сбора информации? Если вы вызываете метод, например GetFiles(), вы уже не знаете, что все возвращаемое является файлом?)

+0

+1 для простого решения. – wheaties

+0

+2 :) .......... – OscarRyz

+0

Вы правы, что я могу быть ленивым и загружать папки только тогда, когда пользователь запрашивает их, но тогда будет пауза каждый раз, когда пользователь попытается открыть новая папка, которая может заставить все дерево чувствовать себя вялым. Это компромисс между долгое время в начале и медленным все время ... но, возможно, это будет не так уж плохо. Я должен проверить это. Спасибо за идеи! –

10

Оборонительный код танки isXYZ() методов, и это, как правило, хорошая практика. Однако иногда производительность низкая, как вы обнаружили.

Альтернативный подход состоит в том, чтобы предположить, что файл является файлом, он существует, он видимый, читаемый и т. Д., И просто попробуйте его прочитать. Если это не так, вы получите исключение, которое вы можете поймать, а затем выполните проверки, чтобы точно выяснить, что пошло не так. Таким образом, вы оптимизируете общий случай (т. Е. Все в порядке), и выполняйте только медленные операции, когда все идет не так.

+0

Huh. Это очень интересный способ взглянуть на проблему, но, к сожалению, это не очень применимо в моем случае. То, что я делаю, - это создание дерева, отражающего структуру файла пользователя, поэтому мне нужно выяснить, является ли файл файлом или каталогом. Хотя, я полагаю, я мог бы использовать listFiles(), чтобы сделать это различие ... Спасибо, ты дал мне кое-что о чем подумать! –

+0

+1 Мне нравится мысль об оптимизации «общего случая». Вы все равно можете поддерживать защитные проверки, чтобы обрабатывать, когда что-то не является файлом. – mpeterson

+0

+1 Я только что изменил некоторый рекурсивный код обработки дерева каталогов, чтобы использовать listFiles, чтобы определить, является ли «файл» каталогом; это небольшое ускорение для локальных и локальных подключенных дисков - я не могу тестировать удаленные диски VPN, пока не вернусь домой. Это также более компактный код. –

3

I столкнулся с одной и той же проблемой

Решение для нашего случая было довольно простым: поскольку наша структура каталогов соответствовала стандарту (там, где не было каталога с символом «.» в его имени), я просто следовал стандарту и применял очень простой эвристический подход: «в нашем случае в каталогах нет«. ». символ в его имени ". Эта простая эвристика резко сократила количество раз, когда наше приложение вызывало функцию isDirectory() класса java.io.File.

Возможно, это ваш случай. Возможно, в вашей структуре каталогов вы могли бы знать, является ли файл каталогом только его соглашениями об именах.

+2

Лично я очень неохотно писал код, который основывается на «как все должно быть». Не сказать, что это никогда не будет хорошим решением, но это опасно. Если кто-то не знает стандарт или не следует ему по какой-либо причине, ваш код дает неверные результаты. Одно дело - выдать сообщение об ошибке на плохие данные, а другое - просто потерпеть неудачу. – Jay

+0

Было бы неплохо, если бы у меня был такой стандарт, на который можно положиться, но, к сожалению, я этого не делаю. : o) –

2

Вот пример кода до и после использования listFiles и использования isDirectory для перехода к дереву каталогов (в моем коде используется общий обратный вызов, чтобы на самом деле что-то делать с каждым каталогом и файлом, если бы я был кодированием C#, это было бы делегатом) ,

Как вы можете видеть, подход listFiles на самом деле более компактен и легко понят, а также немного медленнее на локальном диске (950 мс против 1000 мс) и LAN-приводе (26 секунд, против 28 секунд), как для 23 тысячи файлов.

Очень возможно, что для удаленного подключенного накопителя ускорение может быть существенным, но я не могу проверить это с работы. Немного удивительно, что ускорение по-прежнему составляет лишь около 10% в Windows RAS VPN на сетевой диск.

Новый код

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; 
    } 
0

Только в случае, если вы еще не пробовал еще, называя getBooleanAttributes себя и выполнять необходимую маскировку значительно быстрее, если бы вы выполняете несколько проверок в одном файле. Хотя это не идеальное решение (и которое начинает подталкивать ваш код к конкретной платформе), оно может повысить производительность в 3 или 4 раза. Это довольно значительное повышение производительности, хотя оно не так быстро, как это должно быть.

JDK7 java.nio.file.Path функциональность должна помогать подобным вещам совсем немного.

Наконец, если у вас есть какие-либо элементы управления в среде конечного пользователя, предложите своим пользователям настроить антивирусное программное обеспечение, чтобы не сканировать сетевые диски. Многие из крупных AV-решений (не совсем точно, что они решают) включили по умолчанию. Я не знаю, какое влияние это может оказать на различные методы File, но мы обнаружили, что неправильно настроенный anit-virus может вызвать массовые проблемы с задержкой почти в каждом виде доступа к файлам на сетевых ресурсах.

+0

Это было бы действительно быстрее, если бы я мог сам вызвать getBooleanAttributes(), но, к сожалению, я ограничен Java 1.5 и java.io.FileSystem защищен от пакетов! Глубокий барьер абстракции, все время мешающий. : o) –

+0

Вы должны иметь возможность получить метод, используя отражение (см. метод.setAccessible()) Еще один трюк, который может работать (я не уверен, что jvm будет отклонять классы в java.io pacakge, если они не подписаны Sun) было бы создать ваш собственный класс, который находится в той же папке (просто поместите его в папку java.io в своей банке). –

Смежные вопросы