Есть обходной путь для плохой работы в Java на ходьбу огромных каталогов?

голоса
16

Я пытаюсь обрабатывать файлы по одному, которые хранятся в сети. Чтение файлов быстро из-за буферизации не вопрос. Проблема у меня есть только список каталогов в папке. У меня есть по крайней мере 10k файлов в папке по многим папкам.

Производительность супер медленно, так как File.list () возвращает массив вместо итератора. Java уходит и собирает все имена в папке и упаковывает его в массив, прежде чем вернуться.

Запись ошибки для этого http://bugs.sun.com/view_bug.do;jsessionid=db7fcf25bcce13541c4289edeb4?bug_id=4285834 и не имеет работы вокруг. Они просто говорят , что это было исправлено на JDK7.

Несколько вопросов:

  1. Кто-нибудь есть обходной путь для этого узкого места?
  2. Могу ли я пытаться достичь невозможного? Является ли производительность по-прежнему собирается быть бедными, даже если он просто перебирает каталоги?
  3. Могу ли я использовать бета JDK7 строит, которые имеют эту функцию без необходимости строить весь свой проект на нем?
Задан 10/12/2008 в 01:14
источник пользователем
На других языках...                            


10 ответов

голоса
7

Хотя это не очень, я решил такую ​​проблему, как только по конвейеру от Dir / LS в файл, прежде чем начать мое приложение, и переходя в имени файла.

Если вам необходимо сделать это в приложении, вы можете использовать только system.exec (), но было бы создать какую-то гадость.

Ты спрашивал. Первая форма будет невероятно быстро, второй должен быть довольно быстро, как хорошо.

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

РЕДАКТИРОВАТЬ:

30 минут только, чтобы получить список каталогов, ничего себе.

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

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

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

Ничего себе, я просто пошел , чтобы найти синтаксис .exec команды для вас и наткнулся на это, возможно , именно то , что вы хотите ( в нем указан каталог с помощью Exec и «LS» и труб в результате в вашу программу для обработки): хорошая ссылка в Вайбаке (Йорг предоставляется в комментариях , чтобы заменить эту от солнца , которая сломала Oracle)

Во всяком случае, идея проста, но получить код прямо раздражает. Я пойду украсть коды из интернетов и взломать их - BRB


/ **
 * Примечание: Используйте это только в крайнем случае! Это характерная для окон и даже
 * В том, что это не является хорошим решением, но оно должно быть быстрым.
 * 
 * Использовать его, расширить FileProcessor и вызвать processFiles ( «...») со списком
 * Вариантов, если вы хотите их как / с ... Я настоятельно рекомендую / б
 * 
 * Переопределить ProcessFile и он будет вызываться один раз для каждой строки вывода.
 * /
импорт java.io. *;

общественный FileProcessor абстрактного класса
{
   общественный недействительный processFiles (String dirOptions)
   {
      Процесс theProcess = NULL;
      BufferedReader Instream = NULL;

      // вызываем класс Hello
      пытаться
      {
          . TheProcess = Runtime.getRuntime () Exec ( "CMD / с реж" + dirOptions);
      }
      поймать (IOException е)
      {
         System.err.println ( "Ошибка при запуске () метод");
         e.printStackTrace ();  
      }

      // чтение из стандартного потока вывода вызываемой программы
      пытаться
      {
         Instream = новый BufferedReader (
                                новый InputStreamReader (theProcess.getInputStream ()));  
         ProcessFile (inStream.readLine ());
      }
      поймать (IOException е)
      {
         System.err.println ( "Ошибка на inStream.readLine ()");
         e.printStackTrace ();  
      }

   } // конец метода
   / ** Перекрыть этот метод - он будет вызываться один раз для каждого файла * /
   общественная абстрактная аннулируются ProcessFile (String имя файл);


} // конец класса

И спасибо донора кода в IBM

Ответил 10/12/2008 в 01:57
источник пользователем

голоса
4

Альтернатива является файлы служили над другим протоколом. Как я понимаю, вы используете SMB для этого и Java просто пытается перечислить их как обычный файл.

Проблема здесь не может быть Java в одиночку (как же он ведет себя, когда вы открываете этот каталог с помощью проводника X: \ Shared) В моем опыте это также принимать значительно количество времени.

Вы можете изменить протокол к чему-то, как HTTP, только для извлечения имен файлов. Таким образом, вы можете получить список файлов по HTTP (10k линии should't быть слишком много), и пусть сделки с сервера файл листинга. Это было бы очень быстро, так как он будет работать с локальными ресурсами (те, на сервере)

Затем, когда у вас есть список, вы можете обработать их по точности так, как вы делаете прямо сейчас.

Keypoint должен иметь механизм помощи в другой стороне узла.

Является ли это возможно?

Cегодня:

File [] content = new File("X:\\remote\\dir").listFiles();

for ( File f : content ) {
    process( f );
}

Предложенный:

String [] content = fetchViaHttpTheListNameOf("x:\\remote\\dir");

for ( String fileName : content ) {
    process( new File( fileName ) );
}

Сервер HTTP может быть очень маленьким маленьким и простым файлом.

Если это так, как вы его прямо сейчас, что вы делаете, чтобы принести всю информацию 10k файлов на клиентскую машину (я не знаю, сколько из этой информации), когда вам нужно только имя файла для последующей обработки ,

Если обработка происходит очень быстро сейчас это может быть немного замедлилось. Это происходит потому, что информация упреждения больше недоступна.

Дайте ему попробовать.

Ответил 10/12/2008 в 01:36
источник пользователем

голоса
3

Как насчет использования метода File.list (FilenameFilter фильтр) и реализации FilenameFilter.accept (реж файла, имя String) для обработки каждого файла и возвращать ложь.

Я побежал это на Linux VM для каталога с 10K + файлами и потребовался <10 секунд.

import java.io.File;  
import java.io.FilenameFilter;

public class Temp {
    private static void processFile(File dir, String name) {
        File file = new File(dir, name);
        System.out.println("processing file " + file.getName());
    }

    private static void forEachFile(File dir) {
        String [] ignore = dir.list(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                processFile(dir, name);
                return false;
            }
        });
    }

    public static void main(String[] args) {
        long before, after;
        File dot = new File(".");
        before = System.currentTimeMillis();
        forEachFile(dot);
        after = System.currentTimeMillis();
        System.out.println("after call, delta is " + (after - before));
    }  
}
Ответил 22/10/2013 в 15:20
источник пользователем

голоса
3

Я сомневаюсь, что эта проблема относится к сообщению об ошибке вы ссылке. Проблема есть «только» использование памяти, но не обязательно скорость. Если у вас достаточно памяти ошибка, не имеет отношения к вашей проблеме.

Вы должны измерять ли ваша проблема памяти , связанные или нет. Включите журнал сборщиков мусора и использовать, например , gcviewer для анализа использования памяти.

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

Ответил 10/12/2008 в 09:49
источник пользователем

голоса
2

Не-портативное решение было бы сделать собственные вызовы к операционной системе и поток результатов.

Для Linux

Вы можете посмотреть на что - то вроде ReadDir . Вы можете ходить в структуру каталогов , как связанный список и возвращать результаты в пакетном режиме или по отдельности.

Для Windows

В окнах поведение будет очень похоже с использованием FindFirstFile и FindNextFile APIs.

Ответил 10/12/2008 в 01:22
источник пользователем

голоса
1

Если вы на Java 1.5 или 1.6, выкладывая команды «Dir» и разборе стандартный выходной поток на Windows, является вполне приемлемым подходом. Я использовал этот подход в прошлом для сетевых дисков обработки и в целом был намного быстрее, чем ожидание метода родного java.io.File listFiles (), чтобы вернуться.

Конечно, JNI вызов должен быть быстрее и потенциально безопаснее, чем обстрел из команды «Dir». Следующий код JNI может быть использован для получения списка файлов / каталогов с помощью API Windows. Эта функция может быть легко переработана в новый класс, так что абонент может получить пути к файлам пошагово (то есть получить один путь в то время). Например, вы можете реорганизовать код так, что FindFirstFileW вызывается в конструкторе и имеют отдельный метод для вызова FindNextFileW.

JNIEXPORT jstring JNICALL Java_javaxt_io_File_GetFiles(JNIEnv *env, jclass, jstring directory)
{
    HANDLE hFind;
    try {

      //Convert jstring to wstring
        const jchar *_directory = env->GetStringChars(directory, 0);
        jsize x = env->GetStringLength(directory);
        wstring path;  //L"C:\\temp\\*";
        path.assign(_directory, _directory + x);
        env->ReleaseStringChars(directory, _directory);

        if (x<2){
            jclass exceptionClass = env->FindClass("java/lang/Exception");
            env->ThrowNew(exceptionClass, "Invalid path, less than 2 characters long.");
        }

        wstringstream ss;
        BOOL bContinue = TRUE;
        WIN32_FIND_DATAW data;
        hFind = FindFirstFileW(path.c_str(), &data);
        if (INVALID_HANDLE_VALUE == hFind){
            jclass exceptionClass = env->FindClass("java/lang/Exception");
            env->ThrowNew(exceptionClass, "FindFirstFileW returned invalid handle.");
        }


        //HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
        //DWORD dwBytesWritten;


        // If we have no error, loop thru the files in this dir
        while (hFind && bContinue){

          /*
          //Debug Print Statment. DO NOT DELETE! cout and wcout do not print unicode correctly.
            WriteConsole(hStdOut, data.cFileName, (DWORD)_tcslen(data.cFileName), &dwBytesWritten, NULL);
            WriteConsole(hStdOut, L"\n", 1, &dwBytesWritten, NULL);
            */

          //Check if this entry is a directory
            if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
                // Make sure this dir is not . or ..
                if (wstring(data.cFileName) != L"." &&
                    wstring(data.cFileName) != L"..")
                {   
                    ss << wstring(data.cFileName) << L"\\" << L"\n";
                }
            }
            else{
                ss << wstring(data.cFileName) << L"\n";
            }
            bContinue = FindNextFileW(hFind, &data);
        }   
        FindClose(hFind); // Free the dir structure



        wstring cstr = ss.str();
        int len = cstr.size();
        //WriteConsole(hStdOut, cstr.c_str(), len, &dwBytesWritten, NULL);
        //WriteConsole(hStdOut, L"\n", 1, &dwBytesWritten, NULL);
        jchar* raw = new jchar[len];
        memcpy(raw, cstr.c_str(), len*sizeof(wchar_t));
        jstring result = env->NewString(raw, len);
        delete[] raw;
        return result;
    }
    catch(...){
        FindClose(hFind);
        jclass exceptionClass = env->FindClass("java/lang/Exception");
        env->ThrowNew(exceptionClass, "Exception occured.");
    }

    return NULL;
}

Кредит: https://sites.google.com/site/jozsefbekes/Home/windows-programming/miscellaneous-functions

Даже при таком подходе есть еще эффективности, которые будут получены. Если вы сериализации путь к java.io.File, существует огромное падение производительности - особенно если путь представляет собой файл на сетевом диске. Я понятия не имею, что делает Sun / Oracle под капотом, но если вам нужны дополнительные атрибуты файла, кроме пути к файлу (например, размер, мода даты и т.д.), я обнаружил, что следующая функция JNI гораздо быстрее, чем инстанцировании Java .io.File объект в сети путь.

JNIEXPORT jlongArray JNICALL Java_javaxt_io_File_GetFileAttributesEx(JNIEnv *env, jclass, jstring filename)
{   

  //Convert jstring to wstring
    const jchar *_filename = env->GetStringChars(filename, 0);
    jsize len = env->GetStringLength(filename);
    wstring path;
    path.assign(_filename, _filename + len);
    env->ReleaseStringChars(filename, _filename);


  //Get attributes
    WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
    BOOL result = GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &fileAttrs);
    if (!result) {
        jclass exceptionClass = env->FindClass("java/lang/Exception");
        env->ThrowNew(exceptionClass, "Exception Occurred");
    }

  //Create an array to store the WIN32_FILE_ATTRIBUTE_DATA
    jlong buffer[6];
    buffer[0] = fileAttrs.dwFileAttributes;
    buffer[1] = date2int(fileAttrs.ftCreationTime);
    buffer[2] = date2int(fileAttrs.ftLastAccessTime);
    buffer[3] = date2int(fileAttrs.ftLastWriteTime);
    buffer[4] = fileAttrs.nFileSizeHigh;
    buffer[5] = fileAttrs.nFileSizeLow;

    jlongArray jLongArray = env->NewLongArray(6);
    env->SetLongArrayRegion(jLongArray, 0, 6, buffer);
    return jLongArray;
}

Вы можете найти полный рабочий пример этого JNI подхода , основанного на javaxt-ядро библиотеки. В моих тестах с использованием Java 1.6.0_38 с хостом Windows , ударяя долю Windows, я нашел этот подход JNI примерно в 10 раз быстрее , вызывая java.io.File listFiles () или выкладывая команду «Dir».

Ответил 27/01/2013 в 18:30
источник пользователем

голоса
1

Если вам нужно в конечном счете обрабатывать все файлы, затем с Iterable над String [] не даст вам никаких преимуществ, так как вы все равно должны пойти и забрать весь список файлов.

Ответил 10/12/2008 в 13:26
источник пользователем

голоса
0

Вы уверены, что это связано с Java, а не только общая проблема с наличием 10k записей в одной директории, особенно по сети?

Вы пробовали писать программу корректуры из-концепции, чтобы сделать то же самое в C, используя win32 FindFirst / FindNext функции, чтобы увидеть, является ли это быстрее?

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

Имея 10k строк в массиве звучит как то, что не следует облагать налог современной виртуальной машины Java слишком много либо.

Ответил 01/03/2009 в 09:06
источник пользователем

голоса
0

Использование Iterable не означает, что файлы будут передаваться на вас. На самом деле его обычно наоборот. Таким образом, массив, как правило, быстрее, чем Iterable.

Ответил 01/03/2009 в 08:56
источник пользователем

голоса
0

Интересно, почему есть 10k файлы в каталоге. Некоторые файловые системы не очень хорошо работают с таким количеством файлов. Есть специфика ограничения для файловых систем, таких как максимальное количество файлов в каталог и максимальное количество уровней подкаталога.

Я решить подобную проблему с раствором итератора.

Мне нужно идти на огромные directorys и несколько уровней дерева каталогов рекурсивно.

Я стараюсь FileUtils.iterateFiles () из Apache Commons IO. Но реализовать итератор, добавив все файлы в списке, а затем возвращение List.iterator (). Это очень плохо для памяти.

Так что я предпочитаю, чтобы написать что-то вроде этого:

private static class SequentialIterator implements Iterator<File> {
    private DirectoryStack dir = null;
    private File current = null;
    private long limit;
    private FileFilter filter = null;

    public SequentialIterator(String path, long limit, FileFilter ff) {
        current = new File(path);
        this.limit = limit;
        filter = ff;
        dir = DirectoryStack.getNewStack(current);
    }

    public boolean hasNext() {
        while(walkOver());
        return isMore && (limit > count || limit < 0) && dir.getCurrent() != null;
    }

    private long count = 0;

    public File next() {
        File aux = dir.getCurrent();
        dir.advancePostition();
        count++;
        return aux;
    }

    private boolean walkOver() {
        if (dir.isOutOfDirListRange()) {
            if (dir.isCantGoParent()) {
                isMore = false;
                return false;
            } else {
                dir.goToParent();
                dir.advancePostition();
                return true;
            }
        } else {
            if (dir.isCurrentDirectory()) {
                if (dir.isDirectoryEmpty()) {
                    dir.advancePostition();
                } else {
                    dir.goIntoDir();
                }
                return true;
            } else {
                if (filter.accept(dir.getCurrent())) {
                    return false;
                } else {
                    dir.advancePostition();
                    return true;
                }
            }
        }
    }

    private boolean isMore = true;

    public void remove() {
        throw new UnsupportedOperationException();
    }

}

Обратите внимание, что итератор остановка по количеству файлов iterateds и имеет FileFilter также.

И DirectoryStack является:

public class DirectoryStack {
    private class Element{
        private File files[] = null;
        private int currentPointer;
        public Element(File current) {
            currentPointer = 0;
            if (current.exists()) {
                if(current.isDirectory()){
                    files = current.listFiles();
                    Set<File> set = new TreeSet<File>();
                    for (int i = 0; i < files.length; i++) {
                        File file = files[i];
                        set.add(file);
                    }
                    set.toArray(files);
                }else{
                    throw new IllegalArgumentException("File current must be directory");
                }
            } else {
                throw new IllegalArgumentException("File current not exist");
            }

        }
        public String toString(){
            return "current="+getCurrent().toString();
        }
        public int getCurrentPointer() {
            return currentPointer;
        }
        public void setCurrentPointer(int currentPointer) {
            this.currentPointer = currentPointer;
        }
        public File[] getFiles() {
            return files;
        }
        public File getCurrent(){
            File ret = null;
            try{
                ret = getFiles()[getCurrentPointer()];
            }catch (Exception e){
            }
            return ret;
        }
        public boolean isDirectoryEmpty(){
            return !(getFiles().length>0);
        }
        public Element advancePointer(){
            setCurrentPointer(getCurrentPointer()+1);
            return this;
        }
    }
    private DirectoryStack(File first){
        getStack().push(new Element(first));
    }
    public static DirectoryStack getNewStack(File first){
        return new DirectoryStack(first);
    }
    public String toString(){
        String ret = "stack:\n";
        int i = 0;
        for (Element elem : stack) {
            ret += "nivel " + i++ + elem.toString()+"\n";
        }
        return ret;
    }
    private Stack<Element> stack=null;
    private Stack<Element> getStack(){
        if(stack==null){
            stack = new Stack<Element>();
        }
        return stack;
    }
    public File getCurrent(){
        return getStack().peek().getCurrent();
    }
    public boolean isDirectoryEmpty(){
        return getStack().peek().isDirectoryEmpty();
    }
    public DirectoryStack downLevel(){
        getStack().pop();
        return this;
    }
    public DirectoryStack goToParent(){
        return downLevel();
    }
    public DirectoryStack goIntoDir(){
        return upLevel();
    }
    public DirectoryStack upLevel(){
        if(isCurrentNotNull())
            getStack().push(new Element(getCurrent()));
        return this;
    }
    public DirectoryStack advancePostition(){
        getStack().peek().advancePointer();
        return this;
    }
    public File[] peekDirectory(){
        return getStack().peek().getFiles();
    }
    public boolean isLastFileOfDirectory(){
        return getStack().peek().getFiles().length <= getStack().peek().getCurrentPointer();
    }
    public boolean gotMoreLevels() {
        return getStack().size()>0;
    }
    public boolean gotMoreInCurrentLevel() {
        return getStack().peek().getFiles().length > getStack().peek().getCurrentPointer()+1;
    }
    public boolean isRoot() {
        return !(getStack().size()>1);
    }
    public boolean isCurrentNotNull() {
        if(!getStack().isEmpty()){
            int currentPointer = getStack().peek().getCurrentPointer();
            int maxFiles = getStack().peek().getFiles().length;
            return currentPointer < maxFiles;
        }else{
            return false;
        }
    }
    public boolean isCurrentDirectory() {
        return getStack().peek().getCurrent().isDirectory();
    }
    public boolean isLastFromDirList() {
        return getStack().peek().getCurrentPointer() == (getStack().peek().getFiles().length-1);
    }
    public boolean isCantGoParent() {
        return !(getStack().size()>1);
    }
    public boolean isOutOfDirListRange() {
        return getStack().peek().getFiles().length <= getStack().peek().getCurrentPointer();
    }

}
Ответил 10/12/2008 в 13:18
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more