Использование нескольких ядер требует явного раскрытия параллелизма на уровне потоков для ОС, что обычно требует от программиста написания многопоточной программы. (Или запускать однопотоковую программу несколько раз на разных входах, например, при компиляции make -j4
)
Однако компиляторы для некоторых языков поддерживают автоматическое распараллеливание. Например, C или C ++ с OpenMP могут скомпилировать обычный for()
цикл в программу, которая запускает несколько потоков.
#pragma omp parallel for
for(int i = 0; i < 1000000; ++i)
{
A[i] = B[i] * constant + C[i];
}
Но все же это должно произойти, когда вы написали или скомпилировали программу. Современное оборудование и операционные системы не могут использовать несколько ядер для ускорения однопоточной программы.
Связанный: Как один поток работает на нескольких ядрах? : answer: они этого не делают. Но есть и другие виды параллелизма, такие как параллелизм на уровне инструкций, который одно ядро ЦПУ находит и использует для запуска одного потока быстрее, чем одна инструкция за раз.
Мой ответ на этот вопрос касается некоторых деталей того, как современные процессоры находят и используют детализированный параллелизм на уровне команд. (В основном с упором на x86). Это лишь часть того, как работают нормальные процессоры, когда в полете одновременно выполняется несколько инструкций, и это не то, что вам нужно специально включать. (Существуют счетчики производительности, которые позволяют увидеть, сколько инструкций за такт удалось выполнить вашему ЦП во время выполнения программы или других мер.)
Обратите внимание, что RPi3 использует процессорные ядра ARM Cortex-A53 . Каждое ядро имеет суперскалярную ширину 2 (2 инструкции за такт, как позволяет ILP), но не может переупорядочивать команды, чтобы найти больше параллелизма на уровне команд и скрыть задержку.
Тем не менее, ЦП конвейерно, поэтому общее количество команд в полете (от выборки и декодирования до стадии обратной записи в конце конвейера) является значительным. Когда зависимости данных не ограничивают, на каждом этапе конвейера, на котором работает ЦП, могут быть 2 инструкции с пропускной способностью 2 инструкции в такт. (Это то, что означает 2 в ширину.)
Он не может выполнять инструкции не по порядку, но при аккуратном упорядочении команд (обычно компилятором) он все еще может скрывать задержку команды, которая занимает несколько циклов для ее вывода, чтобы быть готовым. (например, загрузка, даже если она попадет в кэш или умножится, займет несколько циклов, а добавление будет готово в следующем цикле). Хитрость заключается в том, чтобы упорядочить ассемблерные инструкции, чтобы было несколько независимых инструкций между той, которая дает результат, и той, которая его использует.
Наличие программного обеспечения (компилятора) статически запланированных инструкций более хрупко, чем наличие оборудования, которое может переупорядочивать внутренне, сохраняя иллюзию работы в программном порядке. Компиляторам очень трудно выполнить такую же хорошую работу, как даже небольшое окно с неупорядоченным порядком для переупорядочения инструкций, потому что ошибки в кэше непредсказуемы, и трудно анализировать цепочки зависимостей между вызовами функций во время компиляции. И количество регистров ограничено без аппаратного переименования регистров.
Все это не очень удобно, когда ваш код работает медленнее, чем вам хотелось бы. Конечно, в Cortex-A53 под капотом много классных вещей, но в Cortex-A57 под капотом есть больше классных вещей (например, выполнение не по порядку до 3 инструкций за такт), и даже больше в большой процессор x86, такой как Skylake (не говоря уже о разнице в тактовой частоте).
Cortex-A53 довольно фантастичен по сравнению с https://en.wikipedia.org/wiki/Classic_RISC_pipeline, как оригинальные MIPS, о которых вы узнаете в классе компьютерной архитектуры, но по современным стандартам это довольно низкоуровневая версия.