Удивительные результаты параллельного ввода-вывода SSD и HDD

У меня возникла очень странная ситуация с некоторыми из моих тестов, касающихся параллельного ввода-вывода. Вот ситуация. У меня есть несколько потоков, открывающих обработчик файлов для одного и того же файла и считывающих из нескольких мест файла (с равными интервалами) конечное количество байтов и выгружающих их в массив. Все делается с помощью потоков boost. Теперь я предполагаю, что с жестким диском это должно быть медленнее из-за поиска произвольного доступа. Вот почему мои тесты на самом деле нацелены на SSD. Оказывается, я почти не получаю никакого ускорения при чтении одного и того же файла с твердотельного диска по сравнению с жестким диском. Интересно, в чем может быть проблема? Это кажется очень удивительным только мне? Я также публикую свой код ниже, чтобы увидеть, что я именно делаю:

    void readFunctor(std::string pathToFile, size_t filePos, BYTE* buffer, size_t buffPos, size_t dataLn, boost::barrier& barier) {

        FILE* pFile;
        pFile = fopen(pathToFile.c_str(), "rb");

        fseek(pFile, filePos, SEEK_SET);
        fread(buffer, sizeof(BYTE), dataLn, pFile);

        fclose(pFile);
        barier.wait();

    }

    void joinAllThreads(std::vector<boost::shared_ptr<boost::thread> > &threads) {

        for (std::vector<boost::shared_ptr<boost::thread> >::iterator it = threads.begin(); it != threads.end(); ++it) {
            (*it).get()->join();

        }

    }

    void readDataInParallel(BYTE* buffer, std::string pathToFile, size_t lenOfData, size_t numThreads) {
        std::vector<boost::shared_ptr<boost::thread> > threads;
        boost::barrier barier(numThreads);
        size_t dataPerThread = lenOfData / numThreads;

        for (int var = 0; var < numThreads; ++var) {
            size_t filePos = var * dataPerThread;
            size_t bufferPos = var * dataPerThread;
            size_t dataLenForCurrentThread = dataPerThread;
            if (var == numThreads - 1) {
                dataLenForCurrentThread = dataLenForCurrentThread + (lenOfData % numThreads);
            }

            boost::shared_ptr<boost::thread> thread(
                    new boost::thread(readFunctor, pathToFile, filePos, buffer, bufferPos, dataLenForCurrentThread, boost::ref(barier)));
            threads.push_back(thread);

        }

        joinAllThreads(threads);

    }

Теперь .. в моем основном файле у меня почти есть ..:

    int start_s = clock();
    size_t sizeOfData = 2032221073;
    boost::shared_ptr<BYTE> buffer((BYTE*) malloc(sizeOfData));
    readDataInParallel(buffer.get(), "/home/zahari/Desktop/kernels_big.dat", sizeOfData, 4);
    clock_t stop_s = clock();
    printf("%f %f\n", ((double) start_s / (CLOCKS_PER_SEC)) * 1000, (stop_s / double(CLOCKS_PER_SEC)) * 1000);

Удивительно, но при чтении с SSD я не получаю никакого ускорения по сравнению с HDD? Почему это может быть?


person Zahari    schedule 16.07.2013    source источник
comment
Он может сначала записать в выходной буфер? Что ты имеешь в виду ? Зачем ему писать в выходной буфер, и даже если это так, какое это имеет значение?   -  person Zahari    schedule 16.07.2013
comment
Примите во внимание ограничивающие факторы — насколько фрагментирован файл на жестком диске? будут ли случайные секции находиться на одном (или рядом) цилиндре? какова пиковая пропускная способность (по сравнению с пропускной способностью соединения SATA)? Что еще использует диски?   -  person Rowland Shaw    schedule 16.07.2013
comment
В частности, был отформатирован SSD, и этот файл действительно был записан на него первым. Кроме того, выполняя некоторые тесты Linux (с помощью встроенного инструмента для работы с дисками), средняя скорость чтения с диска в 4 раза выше, чем у жесткого диска. И снова мой код не работает быстрее. И больше ничего не использует SSD.   -  person Zahari    schedule 16.07.2013
comment
Все операционные системы кэшируют файлы в памяти. Если вы запускаете тест более одного раза, вы получаете файлы из памяти, а не с жесткого диска или твердотельного накопителя.   -  person brian beuning    schedule 16.07.2013
comment
Я запускал его не раз, да. Но опять же, даже при первом запуске, как я уже сказал.. Быстрее не идет.   -  person Zahari    schedule 16.07.2013
comment
Файлы данных какого размера вы используете? 1 КБ, 1 МБ, 1 ГБ?   -  person brian beuning    schedule 16.07.2013
comment
@zahan Когда вы когда-либо создавали файлы данных, ОС помещала их в кеш файлового буфера. Если вы не загрузились или не сбросили кеш.   -  person brian beuning    schedule 16.07.2013
comment
Нет одновременного доступа к одному и тому же диску из нескольких потоков. Потоки будут конкурировать за общий ресурс, и это станет основным узким местом. Попробуйте синхронизировать доступ к диску с мьютексом, чтобы получить более чистые тесты.   -  person SChepurin    schedule 16.07.2013
comment
Итак, из того, что я здесь получаю, нет реального смысла в реализации параллельного ввода-вывода даже для SSD-дисков. Я уверен, что массовая рандомизация позиций чтения, как это предлагается, покажет некоторое улучшение чтения SSD. Однако моя цель состояла в том, чтобы использовать это для реального ввода-вывода, чтения из файла, где каждый поток будет читать последовательный фрагмент файла. Это даже того стоит? Получу ли я от этого пользу? Я думал, что, учитывая характер SSD, это значительно повысит производительность моего ввода-вывода, но, похоже, я ошибался.   -  person Zahari    schedule 16.07.2013
comment
Из «Параллельного ввода-вывода для высокопроизводительных вычислений» Джона М., май 2001 г. — Основным методом параллельного ввода-вывода является чередование дисков... Компьютер, записывающий большое количество данных, может разделить данные на части и записать их одновременно. для разделения дисков в дисковом массиве. Данные обычно делятся на блоки фиксированного размера, и блоки циклически распределяются по дискам. С тех пор для обычных дисков мало что изменилось. Google использует эту технику в своих центрах обработки данных.   -  person SChepurin    schedule 16.07.2013


Ответы (4)


Ваш файл, вероятно, кэшируется, поэтому вы измеряете накладные расходы ЦП, а не ввод-вывод. Вместо того, чтобы сбрасывать весь кеш диска, вы можете вызвать posix_fadvise() для файла перед его чтением с флагом «wontneed», чтобы посоветовать ядру не кэшировать его. То есть, если вы используете какую-то платформу * nix или Mac OS.

person aldanor    schedule 21.11.2013

Возможным объяснением этого является то, что вы не работаете с настройкой SATA III. Используемый вами твердотельный накопитель SATA III 6 Гбит/с подключен к более старому контроллеру SATA II 3 Гбит/с на материнской плате. В этом случае скорость вашего SSD снижается до 3 Гбит/с.

Проверьте конфигурацию вашего оборудования. Если это SATA II, вам необходимо заменить Mobo, чтобы ваш SSD полностью реализовал свой потенциал производительности.

Также проверьте свой жесткий диск, чтобы узнать, является ли он SATA, SATA II или SATA III.

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

person Kerry Kobashi    schedule 22.09.2015

В зависимости от размера ваших данных, на SSD или HDD, ОС будет кэшировать ваш файл. Так что, вероятно, вы на самом деле не обращаетесь к своим дискам, а к памяти.

person LS_ᴅᴇᴠ    schedule 16.07.2013
comment
Есть ли способ заставить этого не происходить? - person Zahari; 16.07.2013
comment
Я не уверен. В любом случае, это должно зависеть от ОС. - person LS_ᴅᴇᴠ; 16.07.2013
comment
Обычно я делаю размер теста в 2-4 раза больше размера памяти. Или в Linux скажите ОС удалить кеш. Обычно вы также можете указать ОС использовать прямой ввод-вывод или небуферизованное чтение и запись. - person drescherjm; 16.07.2013
comment
@drescherjm Извините за мое невежество, но как сказать ОС удалить кеш? - person Zahari; 17.07.2013
comment
он использует int setvbuf(FILE *stream, char *buf, int mode, size_t size); - person Zahari; 17.07.2013
comment
Это вероятная причина. Размер файла всего 2 Гб. ОС будет хранить его в памяти, поэтому вы, по сути, просто измеряете чтение из памяти, а не с диска. Используйте метод, описанный drescherjm, перед каждым тестовым запуском, если вы работаете в Linux. - person nos; 10.01.2014

В ваших измерениях преобладает шаблонная настройка четырех потоков, каждый из которых выполняет одно чтение, а затем завершается, когда последний из четырех потоков выполняет barier.wait().

Чтобы измерить производительность, каждый поток должен выполнять тысячи однобайтовых операций чтения в цикле перед завершением.

Вот мои предложения по изменению:

   void readFunctor(std::string pathToFile, size_t filePos, BYTE* buffer, size_t buffPos, size_t dataLn) 
   {

       FILE* pFile;
       pFile = fopen(pathToFile.c_str(), "rb");

       fseek(pFile, filePos, SEEK_SET);
       // initialize random number generation
       std::random_device rd;
       tr1::uniform_int_distribution<> randomizer(0, dataLn-1);

       for (int i=0; i<dataLn; i++)
       {
           fseek(pFile, filePos+randomizer(rd), SEEK_SET);
           fread(buffer++, sizeof(BYTE), 1, pFile);
       }

       fclose(pFile);
    }
person ogni42    schedule 16.07.2013
comment
Хорошо, так что вы предлагаете с точки зрения изменения моего кода. И в чем разница, в частности, между чтением тысячи байтов сразу с помощью fread(buffer, sizeof(BYTE), dataLn, pFile); по сравнению с выполнением этой тысячи раз и чтением одного байта каждый раз? - person Zahari; 16.07.2013
comment
Вызов fread в вашем коде приводит к чтению одного большого блока, который хорошо работает даже на жестком диске. выполнение случайного чтения параллельно одиночных байтов полностью отличается от этого. Я постараюсь опубликовать идею в отдельном ответе... - person ogni42; 16.07.2013
comment
Таким образом, включение этого в цикл теоретически снизит производительность на жестком диске независимо от физического расположения файла? - person Zahari; 16.07.2013
comment
Опять же, даже в цикле результаты таковы: 44190 мс для жесткого диска против 44760 мс для твердотельного накопителя. Странно, вам не кажется? - person Zahari; 16.07.2013
comment
На какой платформе вы это запускаете? Какой уровень оптимизации вы используете для компилятора? - person ogni42; 16.07.2013
comment
Я работаю на Centos 6.3 x64 и никак не настраиваю компилятор. - person Zahari; 16.07.2013
comment
попробуйте запустить g++ с оптимизацией -O2 (по крайней мере) - person ogni42; 16.07.2013
comment
Плакат говорит, каждый поток должен создавать тысячи однобайтовых . Если при каждом чтении считывается только один байт, вы, скорее всего, будете измерять накладные расходы API/ОС, чем фактическая скорость чтения. Вы должны считывать более крупные фрагменты, чтобы амортизировать эти накладные расходы до незначительной суммы. - person Ira Baxter; 21.11.2013