История изменений
Исправление bonta, (текущая версия) :
Какая ОС? Какое именно IO? Один и тот же файл или разные?
На моем компьютере (с АМД) Windows 8.1, на удалённых Windows 10. На момент запуска на всех антивирус отключён (иначе с ним очень медленный IO когда много файлов).
Попробуй запустить на втором проце, но запрети занимать больше 6 ядер.
Хорошая мысль.
вполне вероятно что дело не в проце а в том что io-драйвер захлёбывается таким режимом работы.
Думаю вряд ли драйвер захлёбывается, т.к. я смотрел в мониторе ресурсов, по IO – там далеко до захлёбывания было.
А вообще не понял. Ты сначала пишешь про «вроде мютекс» (уточни таки что это конкретно), потом про IO, как они связаны?
Там суть следующем. Имеется класс с методом записи в файл. Метод записи защищён мьютексом на случай одновременного вызова этого метода из разных потоков.
Так же все экземпляры этого класса сообща ведут учёт (отдельные от класса статические структуры) сколько открыто файлов и список, с левого конца которого будет файл в который дольше всего не писали, и с правого конца будет тот в который запись была последней. Это нужно для того чтобы справляться с ограничением среды: в MS Windows (С Runtime Library) есть ограничение на максимальное количество открытых файлов – 512, для всех архитектур. С помощью специальной ф-ии этот лимит можно увеличить до 8192. Но к сожалению софт, который я сопровождаю написан на Embarcadero C++Builder, а в нём своя CRT, в которой этот лимит не меняется, и 512 только для 64 бита, а для 32, будете смеяться, всего 50 файлом можно одновременно открыть. Поэтому появился такой вот механизм обхода этого ограничения, чтобы не открывать-закрывать файлы на каждый чих, а по возможности наиболее «горячие» из них держать открытыми.
И вот эти структуры (единые на все экземпляры) защищены вторым мьютексом. И именно конкуренция за него – бутылочное горлышко. Т.е. когда файлов меньше лимита то работа с этим мьютексом будет только при открытии файлов. Когда очень много файлов (а лимит на открытие я выбрал 30), то между 10 000 потоками начинается борьба за этот мьютекс. Тот кто первый захватил – ищет в списке с лева на право (левее самые старые) есть ли завершившие объекты завершившие работу с файлом (о завершении судит по свободному мьютексу). Если удалось занять мьютекс (уникальный на объект) то файл того объекта закрывается, и поток открывает свой файл. Т.е. в итоге, когда потоков много – все сводится грубо говоря к конкуренции за один мьютекс, а сам IO весьма себя расслаблено чувствует (на ssd).
Кстати я оговорился, не 1000, я запускал на 10 000 потоках. Т.е .все что выше описал – это речь про 10 000 потоков. При этом количество файлов, с которыми они работают, где-то около 1500 тысячи выходит (есть заранее сгенерированные имена, и запускалка потоков, берет случайным образом имена, и получается +- около 1500 файлов на 10 000 потоков).
И по итогу, все эти 1500 тысячи в общем случае будут писаться последовательно, по мере захвата общего на все объекты мьютекса.
Настоящая параллельная запись (с точки зрения прогаммы, а не диска конечно) вернется в программу тогда когда в наборе активных файлов останется 30 (другие объекты перестанут писать в файл, и остнутся 30 или меньше, которые чаще других пишут в файлы).
Т.е. все тормоза именно за борьбу за этот мьютекс.
Конечно в реальной работе этого кода никогда не будет в принципе более 3 потоков, и вряд ли будет более 100 объектов, представляющих файл в программе. Так что в реальных условиях этот код одинаково быстро работает на обоих компах (т.к.по ощущению разницы не видно).
Но вот просто очень интересно во первых почему возникает такая разница. Во вторых (т.к. я никогда не читал про многопоточное программирования, кроме примитивных статей из Хабра) какие существуют методики для решения вот приблизительно той задачи что я решаю. Я использовал мьютексы Си++, потому что это самое простое.
p.s. Хоть сказал про Embarcedero, но это та итоговая программа, а либа сама пишется и тестируется на MSVC, т.е. на нормальном компиляторе, как никто другой лучше подходящем под операционку.
Исходная версия bonta, :
Какая ОС? Какое именно IO? Один и тот же файл или разные?
На моем компьютере (с АМД) Windows 8.1, на удалённых Windows 10. На момент запуска на всех антивирус отключён (иначе с ним очень медленный IO когда много файлов).
Попробуй запустить на втором проце, но запрети занимать больше 6 ядер.
Хорошая мысль.
вполне вероятно что дело не в проце а в том что io-драйвер захлёбывается таким режимом работы.
Думаю вряд ли драйвер захлёбывается, т.к. я смотрел в мониторе ресурсов, по IO – там далеко до захлёбывания было.
А вообще не понял. Ты сначала пишешь про «вроде мютекс» (уточни таки что это конкретно), потом про IO, как они связаны?
Там суть следующем. Имеется класс с методом записи в файл. Метод записи защищён мьютексом на случай одновременного вызова этого метода из разных потоков.
Так же все экземпляры этого класса сообща ведут учёт (отдельные от класса статические структуры) сколько открыто файлов и список, с левого конца которого будет файл в который дольше всего не писали, и с правого конца будет тот в который запись была последней. Это нужно для того чтобы справляться с ограничением среды: в MS Windows (С Runtime Library) есть ограничение на максимальное количество открытых файлов – 512, для всех архитектур. С помощью специальной ф-ии этот лимит можно увеличить до 8192. Но к сожалению софт, который я сопровождаю написан на Embarcadero C++Builder, а в нём своя CRT, в которой этот лимит не меняется, и 512 только для 64 бита, а для 32, будете смеяться, всего 50 файлом можно одновременно открыть. Поэтому появился такой вот механизм обхода этого ограничения, чтобы не открывать-закрывать файлы на каждый чих, а по возможности наиболее «горячие» из них держать открытыми.
И вот эти структуры (единые на все экземпляры) защищены вторым мьютексом. И именно конкуренция за него – бутылочное горлышко. Т.е. когда файлов меньше лимита то работа с этим мьютексом будет только при открытии файлов. Когда очень много файлов (а лимит на открытие я выбрал 30), то между 10 000 потоками начинается борьба за этот мьютекс. Тот кто первый захватил – ищет в списке с лева на право (левее самые старые) есть ли завершившие объекты завершившие работу с файлом (о завершении судит по свободному мьютексу). Если удалось занять мьютекс (уникальный на объект) то файл того объекта закрывается, и поток открывает свой файл. Т.е. в итоге, когда потоков много – все сводится грубо говоря к конкуренции за один мьютекс, а сам IO весьма себя расслаблено чувствует (на ssd).
Кстати я оговорился, не 1000, я запускал на 10 000 потоках. Т.е .все что выше описал – это речь про 10 000 потоков. При этом количество файлов, с которыми они работают, где-то около 1500 тысячи выходит (есть заранее сгенерированные имена, и запускалка потоков, берет случайным образом имена, и получается +- около 1500 файлов на 10 000 потоков).
И по итогу, все эти 1500 тысячи в общем случае будут писаться последовательно, по мере захвата общего на все объекты мьютекса.
Настоящая параллельная запись (с точки зрения прогаммы, а не диска конечно) вернется в программу тогда когда в наборе активных файлов останется 30 (другие объекты перестанут писать в файл, и остнутся 30 или меньше, которые чаще других пишут в файлы).
Т.е. все тормоза именно за борьбу за этот мьютекс.
Конечно в реальной работе этого кода никогда не будет в принципе более 3 потоков, и вряд ли будет более 100 объектов, представляющих файл в программе. Так что в реальных условиях этот код одинаково быстро работает на обоих компах (т.к.по ощущению разницы не видно).
Но вот просто очень интересно во первых почему возникает такая разница. Во вторых (т.к. я никогда не читал про многопоточное программирования, кроме примитивных статей из Хабра) какие существуют методики для решения вот приблизительно той задачи что я решаю. Я использовал мьютексы Си++, потому что это самое простое.