Потоки против одного потока
Всегда ли гарантируется, что многопоточное приложение будет работать быстрее, чем однопоточное?
У меня есть два потока, которые заполняют данные из источника данных, но разные объекты (например, база данных, из двух разных таблиц), похоже, однопоточная версия приложения работает быстрее, чем версия с двумя потоками.
Почему причина будет? когда я смотрю на монитор производительности, оба процессора очень spikey? это связано с переключением контекста?
Каковы лучшие практики, чтобы поднять процессор и полностью использовать его?
Я надеюсь, что это не неоднозначно.
8 ответов
Аналогия может помочь.
У вас есть куча писем, которые вам нужно доставить по разным адресам по всему городу. Итак, вы нанимаете парня с мотоциклом для доставки ваших писем.
Светофоры в вашем городе - идеальные светофоры. Они всегда зеленые, если на перекрестке никого нет.
Парень на мотоцикле мчится вокруг, доставляя кучу писем. Так как на дороге больше никого нет, все огни зеленого цвета, и это здорово. Но вы думаете, эй, это может быть быстрее. Я знаю, я найму другого водителя.
Проблема в том, что ** у вас есть только один мотоцикл *. Так что теперь ваш первый водитель некоторое время ездит на мотоцикле, а затем время от времени останавливается, выходит из строя, а второй водитель подбегает, прыгает и разъезжает.
Это быстрее? Нет, конечно нет. Это медленнее. Добавление большего количества потоков не делает ничего быстрее. Нити не волшебство. Если процессор способен выполнять миллиард операций в секунду, добавление другого потока не делает внезапно доступным еще один миллиард операций в секунду. Скорее, он крадет ресурсы из других потоков. Если мотоцикл может проехать 100 миль в час, остановка велосипеда и включение другого водителя не делает его быстрее! Очевидно, что в среднем письма не доставляются быстрее в этой схеме, они просто доставляются в другом порядке.
Хорошо, а что если вы нанимаете двух водителей и два мотоцикла? Теперь у вас есть два процессора и один поток на процессор, так что это будет быстрее, верно? Нет, потому что мы забыли о светофоре. Раньше был только один мотоцикл, едущий на скорости в любой момент времени. Теперь есть два водителя и два мотоцикла, а это значит, что теперь иногда одному мотоциклу придется ждать, потому что другой находится на перекрестке. Снова, добавление большего количества потоков замедляет вас, потому что вы проводите больше времени, борясь с блокировками. Чем больше процессоров вы добавляете, тем хуже становится; в конечном итоге вы все больше и больше времени проводите в ожидании на красных огнях и все меньше и меньше проводите время за сообщениями.
Добавление большего количества потоков может вызвать отрицательную масштабируемость, если это приводит к утверждению о блокировках. Чем больше потоков, тем больше споров, тем медленнее дела.
Предположим, вы делаете движки быстрее - теперь у вас больше процессоров, больше потоков и более быстрых процессоров. Это всегда делает это быстрее? NO. Часто это не так. Увеличение скорости процессора может замедлить работу многопоточных программ. Опять подумай о трафике.
Предположим, у вас есть город с тысячами водителей и шестидесяти четырьмя мотоциклами, водители бегают туда-сюда между мотоциклами, некоторые мотоциклы на перекрестках блокируют другие мотоциклы. Теперь вы заставляете все эти мотоциклы бегать быстрее. Это помогает? Ну, а в реальной жизни, когда вы едете, вы добираетесь туда, куда вы едете в два раза быстрее в Porsche, чем в Honda Civic? Конечно, нет; Большую часть времени в городе вы находитесь в пробке.
Если вы можете ехать быстрее, часто вы в конечном итоге ждете в пробке дольше, потому что в конечном итоге вы едете в затор быстрее. Если все стремятся к перегруженности быстрее, перегруженность ухудшается.
Многопоточная производительность может быть очень противоречивой. Если вам нужна чрезвычайно высокая производительность, я рекомендую не использовать многопоточное решение, если у вас нет приложения, которое "смущающе параллельно", то есть, какое-то приложение, которое, очевидно, подойдет для запуска нескольких процессоров, например, для вычисления множеств Мандельброта или для трассировки лучей или какая-то такая вещь. И затем, не бросайте больше потоков в проблему, чем у вас есть процессоры. Но для многих приложений запуск большего количества потоков замедляет работу.
Мое мнение
Нет, не гарантируется, что многопоточное приложение будет работать быстрее, чем однопоточное. Основная проблема заключается в правильном распределении рабочей нагрузки по всем доступным ядрам и минимизации блокировки и переключения контекста.
Я думаю, что некоторые из худших вещей, которые могут сделать люди, это пойти и попытаться многопоточность каждого крошечного из своих задач с интенсивным использованием процессора. Иногда они заканчивают тем, что создают сотни потоков, и каждый поток пытается выполнить много интенсивных вычислений ЦП. В этой ситуации лучше всего создать один (или, может быть, два) потока на ядро.
В тех случаях, когда задействован пользовательский интерфейс, почти всегда предпочтительнее делегировать всю интенсивную работу ЦП потокам, чтобы пользовательский интерфейс реагировал. Это, наверное, самое популярное использование для потоков.
... похоже, что однопоточная версия приложения работает быстрее, чем версия с двумя потоками.
Вы провели анализ производительности? Если вы этого не сделали, то то, что вы заметили, несколько не имеет значения.
Каковы лучшие практики, чтобы поднять процессор и полностью использовать его?
Учитывая описание вашей проблемы, не похоже, что ваши проблемы с производительностью связаны с процессором, но связаны с вводом / выводом... ваше взаимодействие с базой данных намного медленнее, чем кеш вашего процессора, и если это сетевая база данных, то это даже медленнее чем ваш жесткий диск. Ваше узкое место в производительности связано с вашей базой данных, поэтому все, что вам нужно сделать, это создать достаточно потоков, чтобы максимизировать пропускную способность вашего соединения с базой данных.
Прямо из Википедии:
преимущества
Некоторые преимущества включают в себя:
- Если поток получает много кеш-пропусков, другой поток (-ы) может продолжить работу, используя преимущества неиспользуемых вычислительных ресурсов, что, таким образом, может привести к более быстрому общему выполнению, поскольку эти ресурсы были бы свободны, если бы выполнялся только один поток,
- Если поток не может использовать все вычислительные ресурсы ЦП (поскольку инструкции зависят от результата друг друга), запуск другого потока позволяет не оставлять эти бездействия.
- Если несколько потоков работают с одним и тем же набором данных, они фактически могут совместно использовать свой кэш, что приводит к лучшему использованию кэша или синхронизации его значений.
Недостатки
Некоторые критические замечания о многопоточности включают в себя:
- Несколько потоков могут создавать помехи друг другу при совместном использовании аппаратных ресурсов, таких как кэши или буферы внешнего просмотра (TLB).
- Время выполнения одного потока не улучшается, но может ухудшаться, даже если выполняется только один поток. Это связано с более медленными частотами и / или дополнительными ступенями конвейера, которые необходимы для размещения аппаратных средств с переключением потоков.
- Аппаратная поддержка многопоточности более заметна для программного обеспечения, поэтому требует больше изменений как прикладных программ, так и операционных систем, чем многопроцессорная обработка.
Обновить
Кроме того, сервер базы данных находится на том же компьютере, на котором выполняется код. это не сервер sql. это ноль дБмс. поэтому, пожалуйста, не принимайте ничего о сервере баз данных.
Некоторые системы NoSQL основаны на диске, и чтение с диска из нескольких потоков почти гарантированно снижает производительность. Жесткий диск может перемещать головку в разные сектора диска при переходе между потоками, и это плохо!
Я понимаю, что вы хотели сказать о скорости ввода-вывода. но это все та же машина. почему IO так медленно?
Ваша система NoSQL может быть основана на диске, поэтому все ваши данные хранятся на диске, а не загружаются в память (например, SQL Server). Кроме того, подумайте об архитектуре: диск является кешем для ОЗУ, ОЗУ кэшируется для кэша ЦП, а кэш ЦП - для регистров ЦП. Итак, Disk -> Ram -> CPU cache -> Registers, есть 3 уровня кэширования, прежде чем вы попадете в регистры. В зависимости от того, сколько данных вы используете, вы можете получить много кеш-пропусков для обоих потоков на каждом из этих уровней... промах кеша в кеш-памяти процессора будет загружать больше данных из ОЗУ, ОЗУ будет загружать больше данных с диска, все это приводит к снижению пропускной способности.
другие критики "создают достаточно потоков, чтобы использовать их". создание множества потоков также потребует времени. право?
Не совсем... у вас есть только две темы. Сколько раз вы создаете темы? Как часто вы их создаете? Если вы создаете только два потока и выполняете всю свою работу в этих двух потоках в течение всего срока службы приложения, то при создании потоков, о которых вы должны беспокоиться, практически не возникает никаких проблем с производительностью.
Если ваша программа интенсивно использует ввод-вывод и тратит большую часть времени на ожидание ввода-вывода (например, операции с базой данных), поэтому многопоточность не будет выполняться быстрее.
Если он делает очень много вычислений в ЦП, так что он будет иметь выгоду или нет, зависит от того, как вы пишете.
Конечно, нет. Потоковая обработка накладывает накладные расходы, поэтому то, насколько она параллельна, зависит от преимуществ приложения.
Согласно закону Амдала, максимальное ускорение зависит от пропорции алгоритма, который можно распараллелить. Если алгоритм сильно параллелен, то увеличение количества процессоров и потоков будет иметь большое увеличение. Если алгоритм не является параллельным (есть много контроля потока кода или конкуренции за данные), тогда нет никакого выигрыша или даже может произойти существующее снижение производительности.
Нет. Потому что, когда вы делаете многопоточность, ваш процессор должен переключаться между потоком, памятью, регистром, и это стоит. Есть некоторые задачи, которые делятся, например, сортировка слиянием, но есть некоторые задачи, которые могут не делиться на подзадачи, такие как проверка, является ли число простым или нет (это просто мой внезапный пример), и затем, если вы попытаетесь отделить его это просто работает как проблема с одним потоком.
Затраты на переключение контекста не являются проблемой, пока у вас не будет сотни потоков. Проблема переключения контекста часто переоценивается (запустите диспетчер задач и сообщите, сколько потоков уже запущено). Наблюдаемые вами пики зависят от сетевого взаимодействия, которое довольно нестабильно по сравнению с вычислениями на локальных процессорах.
Я бы предложил писать масштабируемые приложения в SEDA (Staged Event Driven Architecture), когда система состоит из нескольких (5-15) компонентов, и у каждого компонента есть своя собственная очередь сообщений с ограниченным пулом потоков. Вы можете настроить размер пулов и даже применять алгоритмы, которые изменяют размеры пулов потоков, чтобы сделать некоторые компоненты более производительными, чем другие (поскольку все компоненты используют одни и те же процессоры). Вы можете настроить размер пулов для конкретного оборудования, что делает приложения SEDA чрезвычайно настраиваемыми.
Я видел примеры из реальной жизни, когда код работал так плохо с добавлением большего количества процессоров (ужасный конфликт блокировок между потоками), что системе требовалось удалить процессоры для восстановления производительности; так что да, возможно заставить код работать хуже, добавив больше потоков выполнения.
Приложения с ограниченным вводом-выводом - еще один хороший пример, упомянутый выше.