Производительность карты С++ - Linux (30 секунд) и Windows (30 минут)!

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

using namespace std;

vector<File*> gInputFileList; //Can contain duplicates, File has member sFilename
map<string, File*> gProcessedFileList; //Using map to avoid linear search costs

void processFile(File* pFile)
{
    File* pProcessedFile = gProcessedFileList[pFile->sFilename];
    if(pProcessedFile != NULL)
        return; //Already processed

    foo(pFile); //foo() is the action to do for each file
    gProcessedFileList[pFile->sFilename] = pFile;
}

void main()
{
    size_t n= gInputFileList.size(); //Using array syntax (iterator syntax also gives identical performance)
    for(size_t i=0; i<n; i++){
        processFile(gInputFileList[i]);
    }
}

Код работает правильно, но...

Моя проблема в том, что когда размер ввода составляет 1000, это занимает 30 минут - HALF AN HOUR - в Windows/Visual Studio 2008 Express. Для того же входа требуется всего 40 секунд для запуска Linux/gcc!

В чем может быть проблема? Действие foo() выполняется только очень короткое время, когда используется отдельно. Должен ли я использовать что-то вроде vector:: reserve для карты?

РЕДАКТИРОВАТЬ, ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ

Что делает foo(): 1. он открывает файл 2. читает его в памяти 3. закрывает файл 4. анализируется содержимое файла в памяти 5. он строит список жетонов; Я использую для этого вектор.

Всякий раз, когда я нарушаю программу (во время запуска программы с установленным входом с 1000+ файлами): стек вызовов показывает, что программа находится в середине добавления std::vector.

Ответ 1

В Microsoft Visual Studio существует глобальная блокировка при доступе к стандартной библиотеке С++ для защиты от многопоточной проблемы в сборках Debug. Это может привести к большим результатам. Например, наш полный тестовый код работает на Linux/gcc за 50 минут, тогда как для Windows VС++ 2008 требуется 5 часов. Обратите внимание, что при компиляции в режиме Release этот снимок производительности не существует, используя не-отлаживаемую среду выполнения Visual С++.

Ответ 2

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

Ответ 3

Я очень сильно сомневаюсь, что ваша проблема с производительностью исходит из контейнеров STL.

Попробуйте исключить (закомментировать) вызов foo(pFile) или любой другой метод, который касается файловой системы. Хотя запуск foo(pFile) один раз может появиться быстро, запуск его на 1000 различных файлов (особенно на файловых системах Windows, по моему опыту) может оказаться намного медленнее (например, из-за поведения кэша файловой системы.)

ИЗМЕНИТЬ

Ваш первоначальный пост утверждал, что были повреждены отладочные и выпускные сборки BOTH. Теперь вы снимаете это требование.

Помните, что в файлах DEBUG:

  • Выполняется реализация STL дополнительные проверки и утверждения
  • кучного операции (распределение памяти и т.д.) выполнять дополнительные проверки и утверждения; кроме того, в рамках отладки создается низкая фрагментация кучи отключено (до 10 раз в целом замедление в распределении памяти)
  • не выполняется оптимизация кода, что может привести к дальнейшему STL ухудшение производительности (STL много времени полагается на inlining, разматывание цикла и т.д.).

С 1000 итераций, на которые вы, вероятно, не повлияли выше (не на уровне внешнего цикла, по крайней мере), если вы не используете STL/кучу сильно INSIDE foo().

Ответ 4

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

Ответ 5

Я был бы поражен, если бы проблемы с производительностью, которые вы видите, вообще имели отношение к классу map. Выполнение 1000 поисковых запросов и 1000 входов должно длиться в среднем в миллисекундах. Что делает foo()?

Ответ 6

Не зная, как вписывается остальная часть кода, я думаю, что общая идея кэширования обработанных файлов немного flaky.

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

Ответ 7

Попробуйте комментировать каждый блок или основную операцию, чтобы определить, какая часть фактически вызвала разницу во времени выполнения в Linux и Windows. Я также не думаю, что это было бы из-за карты STL. Проблема может быть внутри foo(). Это может быть в некоторой файловой операции, поскольку это единственное, что я мог подумать, что в этом случае было бы дорогостоящим.

Вы можете вставлять вызовы clock() между операциями, чтобы получить представление о времени выполнения.

Ответ 8

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

Возможно, вы случайно создали алгоритм Shlemiel the Painter.

Ответ 9

Вы можете немного улучшить ситуацию, если вы отбросите карту и разделите свой вектор. Это подразумевает переупорядочение списка входных файлов. Это также означает, что вам нужно найти способ быстрого определения того, был ли файл уже обработан, возможно, удерживая флаг в классе File. Если будет нормально переупорядочить список файлов, и если вы можете сохранить этот грязный флаг в объекте File, то вы можете повысить производительность от O (n log m) до O (n), для n общих файлов и m обработанных файлов.

#include <algorithm>
#include <functional>
// ...
vector<File*>::iterator end(partition(inputfiles.begin(), inputfiles.end(),
                                      not1(mem_fun(&File::is_processed))));
for_each(inputfiles.begin(), end, processFile);

Если вы не можете изменить порядок имен файлов или не можете изменить объект File, тогда вы можете переключить map с помощью vector и затенять каждый файл в списке входных файлов, указав флаг в второй вектор с тем же индексом. Это будет стоить вам O (n) пространства, но даст вам O (1) проверку на наличие грязного состояния.

vector<File*> processed(inputfiles.size(), 0);

for( vector<File*>::size_type i(0); i != inputfiles.size(); ++i ) {
    if( processed[i] != 0 ) return;  // O(1)
    // ...
    processed[i] = inputfiles[i];    // O(1)
}

Но будьте осторожны: вы имеете дело с двумя разными указателями, указывающими на один и тот же адрес, и для случая каждой пары указателей в двух контейнерах. Убедитесь, что один и только один указатель владеет указателем.

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

Ответ 10

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