2008-08-28 2 views
50

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

И было бы это иначе, если бы ядра были распределены по нескольким физическим процессорам? Как и в случае, если у меня была машина с двумя четырёхъядерными процессорами, есть ли какие-либо особые соображения или восемь ядер на двух матрицах считаются равными в Windows?

ответ

51

Не беспокойтесь об этом.

Вместо этого используйте Thread Pool. Пул потоков - это механизм (фактически класс) структуры, который вы можете запросить для нового потока.

Когда вы запрашиваете новый поток, он либо даст вам новый, либо закроет работу, пока поток не будет освобожден. Таким образом, структура отвечает за принятие решения о том, что она должна создавать больше потоков или не зависит от количества существующих процессоров.

Редактировать: Кроме того, как уже упоминалось, ОС отвечает за распределение потоков между различными ЦП.

+1

Что делать, если у вас нет» t получил .NET? – Crashworks 2009-02-20 00:59:57

+52

Это вопрос, связанный с .NET. Почему у вас не было .NET? – 2009-03-03 20:49:57

1

Где каждая нить идет, как правило, обрабатывается самой ОС ... так что сгенерируйте 4 потока в четырехъядерной системе, и ОС решит, какие ядра запускать каждый, что обычно будет 1 нить на каждом ядре.

1

Задача операционной системы состоит в том, чтобы разделить потоки по разным ядрам, и она будет делать это автоматически, когда ваши потоки будут использовать много процессорного времени. Не беспокойтесь об этом. Что касается определения количества ядер вашего пользователя, попробуйте Environment.ProcessorCount в C#.

2

Вам не придется беспокоиться о том, чтобы сделать это самостоятельно. У меня многопоточные .NET-приложения, работающие на двухъядерных машинах, и независимо от того, как запускаются потоки, будь то через ThreadPool или вручную, я вижу приятное равномерное распределение работы по всем ядрам.

1

Одна из причин, по которой вы не должны (как уже было сказано) попытаться выделить этот материал самостоятельно, заключается в том, что у вас просто недостаточно информации, чтобы делать это правильно, особенно в будущем с помощью NUMA и т. Д.

Если у вас есть поток, предназначенный для чтения, и есть простаивающее ядро, ядро ​​будет запустить вашу нить, не волнуйтесь.

8

В случае с управляемыми потоками сложность выполнения этой операции больше, чем у естественных потоков. Это связано с тем, что потоки CLR напрямую не привязаны к потоку собственной ОС. Другими словами, CLR может переключать поток из собственного потока в собственный поток, который он считает нужным. Функция Thread.BeginThreadAffinity предоставляется для управления управляемым потоком в режиме блокировки с потоком собственной ОС. В этот момент вы можете поэкспериментировать с использованием собственных API-интерфейсов, чтобы дать возможность связать родной поток с потоком. Как все говорят здесь, это не очень хорошая идея. Фактически есть documentation, предполагающий, что потоки могут получать меньше времени обработки, если они ограничены одним процессором или ядром.

Вы также можете исследовать класс System.Diagnostics.Process. Там вы можете найти функцию для перечисления потоков процессов в виде коллекции объектов ProcessThread.У этого класса есть методы для установки ProcessorAffinity или даже для установленного процессора - не уверен, что это такое.

Отказ от ответственности: У меня возникла аналогичная проблема, когда я думал, что CPU (ы) были использованы и исследовали много всего этого; однако, основываясь на всем, что я читал, оказалось, что это была не очень хорошая идея, о чем свидетельствуют комментарии, размещенные здесь. Тем не менее, это все еще интересно и опыт для экспериментов.

17

Это не обязательно так просто, как использование пула потоков.

По умолчанию пул потоков выделяет несколько потоков для каждого ЦП. Поскольку каждый поток, участвующий в работе, которую вы выполняете, имеет затраты (перенаправление задачи, использование очень ограниченного L1, L2 и, возможно, L3-кэша процессора и т. Д.), Оптимальное количество потоков для использования - < = количество доступных процессоров - если только каждый поток не запрашивает службы у других машин, например, высоко масштабируемый веб-сервис. В некоторых случаях, особенно те, которые требуют более жесткого чтения и записи на жестком диске, чем активность процессора, на самом деле вам может быть лучше с 1 потоком, чем с несколькими потоками.

Для большинства приложений и, конечно, для кодирования WAV и MP3 вы должны ограничить количество рабочих потоков количеством доступных CPU. Вот некоторые C# код, чтобы найти число процессоров:

int processors = 1; 
string processorsStr = System.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS"); 
if (processorsStr != null) 
    processors = int.Parse(processorsStr); 

К сожалению, это не так просто, как ограничивая себя к числу процессоров. Вы также должны учитывать производительность контроллера (ов) жесткого диска и дисков (дисков).

Единственный способ найти оптимальное количество потоков - пробная ошибка. Это особенно актуально, если вы используете жесткие диски, веб-службы и т. Д. С жесткими дисками вам может быть лучше не использовать все четыре процессора на вашем четырехъядерном процессоре. С другой стороны, с некоторыми веб-службами вам может быть лучше сделать 10 или даже 100 запросов на процессор.

1

Вы не можете этого сделать, поскольку только для этой операционной системы есть привилегии. Если вы решите это ..... тогда будет сложно закодировать приложения. Потому что тогда вам также нужно позаботиться о межпроцессорной связи. критические разделы. для каждого приложения вам нужно создать собственные семафоры или мьютексы ... к которым операционная система дает общее решение, делая это сама .......

2

Вы можете определенно сделать это, написав рутину внутри вашей программы.

Однако вы не должны пытаться это сделать, поскольку операционная система является лучшим кандидатом на управление этими материалами. Я имею в виду, что программа пользовательского режима не должна пытаться это сделать.

Однако иногда это может быть сделано (для действительно продвинутого пользователя) для достижения балансировки нагрузки и даже для выяснения истинной многопоточной многоядерной проблемы (согласование данных/кеширование кодов ...), поскольку различные потоки будут действительно выполнение на другом процессоре.

Сказав это, если вы все еще хотите достичь, мы можем сделать это следующим образом. Я предоставляю вам псевдо-код для (ОС 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, чтобы узнать больше об этих понятиях.

1

Хотя я согласен с большинством ответов здесь, я думаю, что стоит добавить новое соображение: технология Speedstep.

При выполнении интенсивного однопоточного задания на основе многоядерных процессоров, в моем случае Xeon E5-2430 с 6 реальными ядрами (12 с HT) под сервером Windows 2012, работа была распространена среди всех 12 ядер, используя около 8,33% каждого ядра и никогда не запуская увеличение скорости. Процессор остался на частоте 1,2 ГГц.

Когда я установил привязанность потока к определенному ядру, он использовал ~ 100% от этого ядра, в результате чего процессор достиг максимума на 2,5 ГГц, что более чем удвоило производительность.

Это программа, которую я использовал, которая только петли увеличивает переменную. При вызове с -a он будет устанавливать сродство к ядру 1. Аффинная часть была основана на this post.

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); 
      } 
     } 
    } 
} 

И результаты:

results

скорость процессора, как показано на диспетчер задач, аналогично тому, что отчеты CPU-Z:

enter image description here

Смежные вопросы