Как создать потоки на разных ядрах процессора?



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



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

1299   10  

10 ответов:

Не утруждайте себя этим.

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

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

Изменить: In кроме того, как уже упоминалось, ОС отвечает за распределение потоков между различными процессорами.

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

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

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

вы также можете исследовать

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

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

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

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

результаты:

results

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

enter image description here

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

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

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

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

Если у вас есть поток чтения для запуска, и есть ядро простоя, ядро будет Запустите свой поток, не волнуйтесь.

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

Comments

    Ничего не найдено.