Я уже говорил выше, что, по моему мнению, проблема высосана из пальца, и повторяю это вновь. В подавляющем большинстве случаев важна производительность не одного конкретного потока и даже не одной задачи, а всей системы в целом. А производительность в целом зависит в том числе и от накладных расходов на работу самой системы: чем они выше, тем меньше процессорного времени остаётся для задач, выполняющих полезную (с точки зрения пользователя) работу. Соответственно, слишком "умные" алгоритмы планирования, распределения памяти и т.п. могут в итоге привести к прямо противоположному результату -- к снижению эффективности системы.
Простейшая реализация взаимодействия нескольких потоков, на мой взгляд, достаточно эффективна: каждый поток выполняет свою работу, не зависящую от других потоков, а когда дело доходит до обращения к общим данным, просто запрашивает у ОС захват мьютекса (или другого объекта синхронизации -- не суть важно, это детали). Если мьютекс свободен, ОС отдаёт его потоку и возвращает ему управление, в результате накладные расходы невелики -- один системный вызов, причём простой, не требующий длительной обработки внутри системы, и два переключения контекста (на код ядра и из него обратно на код потока). Если же мьютекс занят, ОС ставит поток в очередь к нему и передаёт процессор следующему готовому к выполнению потоку. Накладные расходы опять-таки минимальны (выполнение простого кода, связанного с управлением мьютексами, и снова два переключения контекста), и почти всё процессорное время уходит на работу потоков, т.е. на полезную работу. Лично я не вижу _никаких_ путей для уменьшения этих накладных расходов: потоки не занимают процессор ни единого лишнего такта (либо делают полезную работу, которую в любом случае придётся делать, либо переводятся системой в состояние ожидания, предоставляя процессор другим потокам, у которых есть работа), а поэтому всё лишнее время уходит лишь на выполнение сравнительно простого кода системы.
Если говорить строго, есть один метод увеличения производительности путём сокращения накладных расходов, но он достигается уменьшением функциональности системы -- а именно отказом от квантования времени. Пока у потока есть, чем заняться, он занимает процессор непрерывно, и снимается с него только тогда, когда переходит в состояние ожидания. Накладные расходы на вызов функций АПИ, связанных с синхронизацией, будут примерно теми же, что в предыдущем случае, но не будет накладных расходов на более сложный планировщик, подсчёт тиков таймера и т.д. и т.п. -- просто переключай потоки при переходе текущего в состояние ожидания, и всё. Но такое решение имеет весьма ограниченное применение и эффективно лишь в определённых ситуациях. Собственно, оно было весьма популярно лишь в эпоху пакетной обработки (когда машине утром скармливали колоду перфокарт, а вечером приходили за результатами). Например, в системе существует два задания: высокоприоритетное, интенсивно использующее ввод-вывод, и низкоприоритетное, загружающее главным образом процессор. Ввысокоприоритетное почти всё время проводит в ожидании завершения начатых им операций ввода-вывода, а в это время низкоприоритетное занимается расчётами, поскольку процессор оказывается свободен. Но, как только операция ввода-вывода заканчивается, управление опять получает высокоприоритетное задание, которое занимает процессор очень ненадолго, запускает очередную операцию ввода-вывода и опять отправляется в ожидание. В результате достигается оптимальная загрузка аппаратуры: и ввод-вывод на полную катушку работает, и процессор без дела не стоит, и накладные расходы на работу самой ОС невелики из-за отсутствия сложных алгоритмов планирования.
|