¿Cómo puedo generar subprocesos en diferentes núcleos de CPU?


Digamos que tenía un programa en C# que hizo algo computacionalmente caro, como la codificación de una lista de archivos WAV en MP3s. Normalmente codificaría los archivos uno a la vez, pero digamos que quería que el programa para averiguar cuántos núcleos de CPU que tenía y girar un hilo de codificación en cada núcleo. Por lo tanto, cuando corro el programa en una CPU de cuatro núcleos, el programa se da cuenta de que es una CPU de cuatro núcleos, se da cuenta de que hay cuatro núcleos para trabajar con, a continuación, genera cuatro hilos para la codificación, cada uno de los cuales se está ejecutando en su propia CPU separada. ¿Cómo haría esto?

¿Y esto sería diferente si los núcleos se extendieran a través de múltiples CPU físicas? Como en, si tuviera una máquina con dos CPU de cuatro núcleos en ella, hay alguna consideración especial o son los ocho núcleos a través de los dos dados considerados iguales en Windows?

Author: Lernkurve, 2008-08-28

10 answers

No te molestes en hacer eso.

En su lugar use el Grupo de subprocesos . El grupo de subprocesos es un mecanismo (en realidad una clase) del marco que puede consultar para un nuevo subproceso.

Cuando pides un nuevo hilo, te dará uno nuevo o encolerizará el trabajo hasta que se libere un hilo. De esa manera, el framework es el encargado de decidir si debe crear más subprocesos o no dependiendo del número de CPU presentes.

Editar: Además, como ya ha sido mencionado, el sistema operativo se encarga de distribuir los hilos entre las diferentes CPU.

 54
Author: Jorge Córdoba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 14:35:50

No es necesariamente tan simple como usar el grupo de subprocesos.

De forma predeterminada, el grupo de subprocesos asigna varios subprocesos para cada CPU. Dado que cada subproceso que se involucra en el trabajo que está haciendo tiene un costo (sobrecarga de conmutación de tareas, uso de la caché L1, L2 y tal vez L3 muy limitada de la CPU, etc...), el número óptimo de subprocesos a usar es

Para la mayoría de las aplicaciones, y ciertamente para la codificación WAV y MP3, debe limitar el número de subprocesos de trabajo al número de CPU disponibles. Aquí hay un código C # para encontrar el número de CPU:

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

Desafortunadamente, no es tan simple como limitarse al número de CPU. tenga en cuenta el rendimiento de los controladores de disco duro(s) y el disco(s).

La única manera de encontrar el número óptimo de hilos es probar un error. Esto es particularmente cierto cuando está utilizando discos duros, servicios web y similares. Con los discos duros, podría ser mejor no usar los cuatro procesadores en su CPU de procesador cuádruple. Por otro lado, con algunos servicios web, podría ser mejor hacer 10 o incluso 100 solicitudes por CPU.

 16
Author: Joe Erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-20 01:54:23

En el caso de los subprocesos administrados, la complejidad de hacer esto es un grado mayor que la de los subprocesos nativos. Esto se debe a que los subprocesos CLR no están directamente vinculados a un subproceso de sistema operativo nativo. En otras palabras, el CLR puede cambiar un subproceso administrado de subproceso nativo a subproceso nativo como considere oportuno. El hilo function .BeginThreadAffinity se proporciona para colocar un subproceso administrado en lock-step con un subproceso de sistema operativo nativo. En ese punto, podría experimentar con el uso de API nativas para dale afinidad al procesador de subprocesos nativo subyacente. Como todo el mundo sugiere aquí, esto no es una muy buena idea. De hecho, existe documentación que sugiere que los subprocesos pueden recibir menos tiempo de procesamiento si están restringidos a un solo procesador o núcleo.

También puede explorar el Sistema .Diagnostico.Process class. Allí puede encontrar una función para enumerar los hilos de un proceso como una colección de objetos ProcessThread. Esta clase tiene métodos para establecer ProcessorAffinity o incluso establecer un procesador preferido not no estoy seguro de lo que es.

Descargo de responsabilidad: He experimentado un problema similar en el que pensé que la CPU(s) estaban infrautilizados e investigado un montón de estas cosas; sin embargo, basado en todo lo que he leído, parecía que no era una muy buena idea, como lo demuestran los comentarios publicados aquí también. Sin embargo, sigue siendo interesante y una experiencia de aprendizaje para experimentar.

 8
Author: Peter Meyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-20 00:52:26

Definitivamente puedes hacer esto escribiendo la rutina dentro de tu programa.

Sin embargo, no debe intentar hacerlo, ya que el Sistema operativo es el mejor candidato para administrar estas cosas. Me refiero a programa de modo de usuario no debe hacer tratar de hacerlo.

Sin embargo, a veces, se puede hacer (para un usuario realmente avanzado) para lograr el equilibrio de carga e incluso para descubrir el verdadero problema multi hilo multi core (data racing/cache coherence...) como diferentes hilos serían realmente ejecutar en procesador diferente.

Dicho esto, si todavía quieres lograrlo, podemos hacerlo de la siguiente manera. Le estoy proporcionando el pseudo código para (sistema operativo Windows), sin embargo, podrían hacerse fácilmente en Linux también.

#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".

Después de llamar a la rutina anterior, los hilos siempre se ejecutarían de la siguiente manera:

Thread1-> Core1
Thread2-> Core2
Thread3-> Core3
Thread4-> Core4
Thread5-> Core5
Thread6-> Core6
Thread7-> Core7
Thread8-> Core8

Thread9-> Core1
Thread10-> Core2
...............

Para obtener más información, consulte manual/MSDN para saber más sobre estos conceptos.

 3
Author: Mantosh Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-29 00:07:18

Aunque estoy de acuerdo con la mayoría de las respuestas aquí, creo que vale la pena agregar una nueva consideración: la tecnología Speedstep.

Cuando se ejecuta un trabajo intensivo de CPU, un solo subproceso en un sistema multinúcleo, en mi caso un Xeon E5-2430 con 6 núcleos reales (12 con HT) en Windows server 2012, el trabajo se extendió entre todos los 12 núcleos, utilizando alrededor del 8.33% de cada núcleo y nunca activando un aumento de velocidad. La CPU se mantuvo a 1,2 GHz.

Cuando establezco la afinidad del hilo a un núcleo, se utiliza ~100% de ese núcleo, haciendo que la CPU a máximo a 2,5 GHz, más que duplicar el rendimiento.

Este es el programa que utilicé, que solo hace bucles aumentando una variable. Cuando se llama con -a, establecerá la afinidad en core 1. La parte de afinidad se basó en este 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);
            }
        }
    }
}

Y los resultados:

resultado

Velocidad del procesador, como se muestra por el administrador de tareas, similar a lo que CPU-Z informa:

introduzca la descripción de la imagen aquí

 3
Author: AlexDev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-01 14:17:56

No debería tener que preocuparse por hacer esto usted mismo. Tengo aplicaciones. NET multiproceso que se ejecutan en máquinas de doble cuatro, y no importa cómo se inicien los subprocesos, ya sea a través del ThreadPool o manualmente, veo una buena distribución uniforme del trabajo en todos los núcleos.

 2
Author: Eric Z Beard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 14:16:11

Donde va cada hilo es generalmente manejado por el sistema operativo itself...so genere 4 subprocesos en un sistema de 4 núcleos y el sistema operativo decidirá en qué núcleos ejecutar cada uno, que generalmente será 1 subproceso en cada núcleo.

 1
Author: Adam Haile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 14:13:01

El trabajo del sistema operativo es dividir los subprocesos en diferentes núcleos, y lo hará automáticamente cuando sus subprocesos estén usando mucho tiempo de CPU. No te preocupes por eso. En cuanto a averiguar cuántos núcleos tiene su usuario, pruebe Environment.ProcessorCount en C#.

 1
Author: wvdschel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 14:13:57

Una de las razones por las que no debería (como se ha dicho) tratar de asignar este tipo de cosas usted mismo, es que simplemente no tiene suficiente información para hacerlo correctamente, particularmente en el futuro con NUMA, etc.

Si tienes un hilo de lectura para ejecutar, y hay un núcleo inactivo, el núcleo ejecutará tu hilo, no te preocupes.

 1
Author: Will Dean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 14:29:39

No puede hacer esto, ya que solo el sistema operativo tiene los privilegios para hacerlo. Si lo decides tú.....entonces será difícil codificar aplicaciones. Porque entonces también necesita cuidar la comunicación entre procesadores. secciones críticas. para cada aplicación tienes que crear tus propios semáforos o mutex......to qué sistema operativo da una solución común al hacerlo por sí mismo.......

 1
Author: Amit Puri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-18 15:19:25