Допустим, у меня есть программа на C#, которая делает что-то затратное в вычислительном отношении, например, кодирует список файлов WAV в MP3. Обычно я кодировал файлы по одному, но, скажем, я хотел, чтобы программа вычисляла, сколько ядер ЦП у меня есть, и запускала поток кодирования на каждом ядре. Итак, когда я запускаю программу на четырехъядерном ЦП, программа определяет, что это четырехъядерный ЦП, выясняет, что есть четыре ядра, с которыми нужно работать, а затем порождает четыре потока для кодирования, каждый из которых выполняется отдельно. ЦПУ. Как бы я это сделал?
И было бы все иначе, если бы ядра были распределены по нескольким физическим процессорам? Например, если бы у меня была машина с двумя четырехъядерными процессорами, есть ли какие-то особые соображения или восемь ядер на двух кристаллах считаются равными в Windows?





То, куда идет каждый поток, обычно обрабатывается самой ОС ... поэтому сгенерируйте 4 потока в 4-ядерной системе, и ОС будет решать, на каких ядрах запускать каждое, что обычно будет по 1 потоку на каждом ядре.
Не беспокойтесь об этом.
Вместо этого используйте Пул потоков. Пул потоков - это механизм (фактически класс) инфраструктуры, который вы можете запрашивать для нового потока.
Когда вы запрашиваете новый поток, он либо выдаст вам новый, либо поставит работу в очередь до тех пор, пока поток не будет освобожден. Таким образом, фреймворк отвечает за принятие решения о том, следует ли создавать больше потоков или нет, в зависимости от количества имеющихся процессоров.
Обновлено: Кроме того, как уже упоминалось, ОС отвечает за распределение потоков между различными процессорами.
более того, включение TPL (Task Parallel) также берет на себя эту работу, вы можете прочитать больше здесь msdn.microsoft.com/en-us/magazine/cc163340.aspx
Задача операционной системы - разделить потоки между разными ядрами, и она будет делать это автоматически, когда ваши потоки используют много процессорного времени. Не беспокойся об этом. Чтобы узнать, сколько ядер у вашего пользователя, попробуйте Environment.ProcessorCount на C#.
Вам не стоит беспокоиться об этом самостоятельно. У меня есть многопоточные приложения .NET, работающие на машинах с двумя квадрокоптерами, и независимо от того, как запускаются потоки, будь то через ThreadPool или вручную, я вижу хорошее равномерное распределение работы по всем ядрам.
Одна из причин, по которой вам не следует (как уже было сказано) пытаться выделить такие вещи самостоятельно, заключается в том, что у вас просто недостаточно информации, чтобы сделать это должным образом, особенно в будущем с NUMA и т. д.
Если у вас есть поток, предназначенный для чтения для выполнения, а ядро простаивает, ядро буду запускает ваш поток, не беспокойтесь.
В случае управляемых потоков сложность этого намного выше, чем у собственных потоков. Это связано с тем, что потоки CLR не связаны напрямую с собственным потоком ОС. Другими словами, CLR может переключать поток удалось с собственного потока на собственный поток по своему усмотрению. Функция Thread.BeginThreadAffinity предназначена для перевода управляемого потока в синхронный режим с собственным потоком ОС. На этом этапе вы можете поэкспериментировать с использованием собственных API-интерфейсов, чтобы придать сходство с базовым процессором собственных потоков. Как здесь все говорят, это не очень хорошая идея. Фактически, документация предполагает, что потоки могут получать меньше времени обработки, если они ограничены одним процессором или ядром.
Вы также можете изучить класс System.Diagnostics.Process. Там вы можете найти функцию для перечисления потоков процесса как коллекции объектов ProcessThread. У этого класса есть методы для установки ProcessorAffinity или даже для установки процессора предпочтительный - не уверен, что это такое.
Заявление об отказе от ответственности: у меня возникла аналогичная проблема, когда я думал, что ЦП недостаточно загружены, и исследовал много этого; однако, исходя из всего, что я прочитал, оказалось, что это не очень хорошая идея, о чем свидетельствуют также размещенные здесь комментарии. Тем не менее, экспериментировать по-прежнему интересно и поучительно.
Это не обязательно так просто, как использование пула потоков.
По умолчанию пул потоков выделяет несколько потоков для каждого процессора. Поскольку каждый поток, который участвует в выполняемой вами работе, имеет затраты (накладные расходы на переключение задач, использование очень ограниченного кеша L1, L2 и, возможно, L3 ЦП и т. д.), Оптимальное количество потоков для использования составляет
Для большинства приложений и, конечно, для кодирования WAV и MP3, вам следует ограничить количество рабочих потоков количеством доступных процессоров. Вот код C# для определения количества процессоров:
int processors = 1;
string processorsStr = System.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS");
if (processorsStr != null)
processors = int.Parse(processorsStr);
К сожалению, это не так просто, как ограничиться количеством процессоров. Вы также должны учитывать производительность контроллера (ов) жесткого диска и диска (ов).
Единственный способ найти оптимальное количество потоков - это пробная ошибка. Это особенно верно, когда вы используете жесткие диски, веб-службы и т. д. С жесткими дисками вам может быть лучше не использовать все четыре процессора на вашем четырехпроцессорном процессоре. С другой стороны, с некоторыми веб-службами вам может быть лучше делать 10 или даже 100 запросов на процессор.
Оптимальное количество используемых потоков немного больше, чем количество процессоров. Ваш аргумент об обратном ошибочен. Если переключение задач происходит из-за того, что поток больше не может продвигаться вперед, у вас будет этот переключатель задач независимо от того, сколько потоков вы создали. Переключение задач с полного использования временного интервала незначительно, поскольку ОС тщательно выбирает временной интервал, чтобы гарантировать это.
вы не можете этого сделать, поскольку только операционная система имеет на это права. Если решишь ..... то приложения будет сложно кодировать. Потому что тогда вам также нужно позаботиться о межпроцессорной связи. критические разделы. для каждого приложения вы должны создать свои собственные семафоры или мьютексы ...... какая операционная система дает общее решение, делая это сама .......
Вы определенно можете сделать это, написав процедуру внутри своей программы.
Однако вам не следует пытаться это сделать, поскольку операционная система - лучший кандидат для управления этими вещами. Я имею в виду, что программа в пользовательском режиме не должна пытаться это сделать.
Однако иногда это можно сделать (для действительно продвинутого пользователя), чтобы достичь балансировки нагрузки и даже выяснить настоящую многопоточную многоядерную проблему (гонка данных / согласованность кеша ...), поскольку разные потоки действительно выполняются на другом процессоре. .
Сказав это, если вы все еще хотите достичь, мы можем сделать это следующим образом. Я предоставляю вам псевдокод для (ОС Windows), однако их можно легко сделать и в Linux.
#define MAX_CORE 256
processor_mask[MAX_CORE] = {0};
core_number = 0;
Call GetLogicalProcessorInformation();
// From Here we calculate the core_number and also we populate the process_mask[] array
// which would be used later on to set to run different threads on different CORES.
for(j = 0; j < THREAD_POOL_SIZE; j++)
Call SetThreadAffinityMask(hThread[j],processor_mask[j]);
//hThread is the array of handles of thread.
//Now if your number of threads are higher than the actual number of cores,
// you can use reset the counters(j) once you reach to the "core_number".
После вызова вышеуказанной подпрограммы потоки всегда будут выполняться следующим образом:
Thread1-> Core1
Thread2-> Core2
Thread3-> Core3
Thread4-> Core4
Thread5-> Core5
Thread6-> Core6
Thread7-> Core7
Thread8-> Core8
Thread9-> Core1
Thread10-> Core2
...............
Для получения дополнительной информации, пожалуйста, обратитесь к руководству / MSDN, чтобы узнать больше об этих концепциях.
Хотя я согласен с большинством ответов здесь, я думаю, что стоит добавить новое соображение: технологию Speedstep.
При выполнении однопоточного задания с интенсивным использованием ЦП в многоядерной системе, в моем случае Xeon E5-2430 с 6 реальными ядрами (12 с HT) под Windows Server 2012, задание распределялось между всеми 12 ядрами, используя около 8,33% каждого ядра и никогда не приводит к увеличению скорости. Процессор остался на 1,2 ГГц.
Когда я устанавливаю привязку потока к определенному ядру, оно использует ~ 100% этого ядра, в результате чего процессор работает на максимальной частоте 2,5 ГГц, что более чем вдвое увеличивает производительность.
Это программа, которую я использовал, которая просто выполняет цикл увеличения переменной. При вызове с -a он устанавливает привязку к ядру 1. Часть привязки была основана на эта почта.
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
namespace Esquenta
{
class Program
{
private static int numThreads = 1;
static bool affinity = false;
static void Main(string[] args)
{
if (args.Contains("-a"))
{
affinity = true;
}
if (args.Length < 1 || !int.TryParse(args[0], out numThreads))
{
numThreads = 1;
}
Console.WriteLine("numThreads:" + numThreads);
for (int j = 0; j < numThreads; j++)
{
var param = new ParameterizedThreadStart(EsquentaP);
var thread = new Thread(param);
thread.Start(j);
}
}
static void EsquentaP(object numero_obj)
{
int i = 0;
DateTime ultimo = DateTime.Now;
if (affinity)
{
Thread.BeginThreadAffinity();
CurrentThread.ProcessorAffinity = new IntPtr(1);
}
try
{
while (true)
{
i++;
if (i == int.MaxValue)
{
i = 0;
var lps = int.MaxValue / (DateTime.Now - ultimo).TotalSeconds / 1000000;
Console.WriteLine("Thread " + numero_obj + " " + lps.ToString("0.000") + " M loops/s");
ultimo = DateTime.Now;
}
}
}
finally
{
Thread.EndThreadAffinity();
}
}
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();
[DllImport("kernel32.dll")]
public static extern int GetCurrentProcessorNumber();
private static ProcessThread CurrentThread
{
get
{
int id = GetCurrentThreadId();
return Process.GetCurrentProcess().Threads.Cast<ProcessThread>().Single(x => x.Id == id);
}
}
}
}
И результаты:

Скорость процессора, показанная диспетчером задач, аналогична той, которую сообщает CPU-Z:

Спасибо за ценную информацию об использовании ЦП. Я постараюсь использовать ваш код для своих нужд
У меня в обоих случаях на iCore-7 примерно 550 млн шлейфов. Консольное приложение по умолчанию является однопоточным. Нам может понадобиться этот docs.microsoft.com/en-us/dotnet/api/…
@PawelCioch В этом случае я специально запускаю только один поток, чтобы увидеть, как один и тот же поток распространяется на разные ядра. Чтобы запустить этот тест на своем ПК, вам необходимо закрыть все приложения, интенсивно использующие ЦП, такие как Visual Studio и браузеры, до тех пор, пока скорость не стабилизируется на низком уровне. Затем, когда вы запустите его с привязкой, вы должны увидеть увеличение скорости в диспетчере задач.
да, после того как я проанализировал приложение, я понял, в чем его цель, очень крутые вещи, все работает и имеет смысл. Да, я смог 100% загрузить 1 ядро или распределить нагрузку по всем ядрам. Спасибо
Это вопрос, связанный с .NET. Почему бы вам не установить .NET?