Pergunta

Digamos que eu tenha um programa em C# que fizesse algo computacionalmente caro, como codificar uma lista de arquivos WAV em MP3s.Normalmente eu codificaria os arquivos um de cada vez, mas digamos que eu quisesse que o programa descobrisse quantos núcleos de CPU eu tinha e criasse um thread de codificação em cada núcleo.Então, quando executo o programa em uma CPU quad core, o programa descobre que é uma CPU quad core, descobre que há quatro núcleos para trabalhar e, em seguida, gera quatro threads para a codificação, cada um deles sendo executado separadamente. CPU.Como eu faria isso?

E isso seria diferente se os núcleos estivessem espalhados por várias CPUs físicas?Por exemplo, se eu tivesse uma máquina com duas CPUs quad core, há alguma consideração especial ou os oito núcleos nas duas matrizes são considerados iguais no Windows?

Foi útil?

Solução

Não se preocupe em fazer isso.

Em vez disso, use o Grupo de discussão.O pool de threads é um mecanismo (na verdade, uma classe) da estrutura que você pode consultar para um novo thread.

Quando você solicita um novo thread, ele lhe dará um novo ou enfileirará o trabalho até que um thread seja liberado.Dessa forma o framework é responsável por decidir se deve ou não criar mais threads dependendo do número de CPUs presentes.

Editar:Além disso, como já foi mencionado, o SO é responsável por distribuir os threads entre as diferentes CPUs.

Outras dicas

Não é necessariamente tão simples quanto usar o pool de threads.

Por padrão, o pool de threads aloca vários threads para cada CPU.Como cada thread envolvido no trabalho que você está realizando tem um custo (sobrecarga de alternância de tarefas, uso do cache L1, L2 e talvez L3 muito limitado da CPU, etc...), o número ideal de threads a serem usados ​​é <= o número de CPUs disponíveis - a menos que cada thread esteja solicitando serviços de outras máquinas - como um serviço web altamente escalável.Em alguns casos, especialmente aqueles que envolvem mais leitura e gravação no disco rígido do que atividade da CPU, você pode ficar melhor com um thread do que com vários threads.

Para a maioria dos aplicativos, e certamente para codificação WAV e MP3, você deve limitar o número de threads de trabalho ao número de CPUs disponíveis.Aqui está um código C# para encontrar o número de CPUs:

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

Infelizmente, não é tão simples quanto limitar-se ao número de CPUs.Você também deve levar em consideração o desempenho do(s) controlador(es) de disco rígido e do(s) disco(s).

A única maneira de realmente encontrar o número ideal de threads é tentar um erro.Isto é particularmente verdadeiro quando você usa discos rígidos, serviços da web e outros.Com discos rígidos, talvez seja melhor não usar todos os quatro processadores em sua CPU de processador quádruplo.Por outro lado, com alguns serviços da web, talvez seja melhor fazer 10 ou até 100 solicitações por CPU.

No caso de threads gerenciados, a complexidade de fazer isso é um grau maior que a dos threads nativos.Isso ocorre porque os threads CLR não estão diretamente vinculados a um thread nativo do sistema operacional.Em outras palavras, o CLR pode mudar um gerenciou thread de thread nativo para thread nativo conforme achar adequado.A função Thread.BeginThreadAffinity é fornecido para colocar um thread gerenciado em lock-step com um thread de sistema operacional nativo.Nesse ponto, você pode experimentar o uso de APIs nativas para fornecer afinidade ao processador de thread nativo subjacente.Como todos sugerem aqui, esta não é uma ideia muito boa.Na verdade existe documentação sugerindo que threads podem receber menos tempo de processamento se estiverem restritos a um único processador ou núcleo.

Você também pode explorar o Sistema.Diagnóstico.Processo aula.Lá você pode encontrar uma função para enumerar os threads de um processo como uma coleção de ProcessThread objetos.Esta classe possui métodos para definir ProcessorAffinity ou até mesmo definir um preferido processador - não tenho certeza do que é isso.

Isenção de responsabilidade:Eu experimentei um problema semelhante em que pensei que as CPUs estavam subutilizadas e pesquisei muito sobre essas coisas;no entanto, com base em tudo o que li, parece que não foi uma ideia muito boa, como evidenciado pelos comentários postados aqui também.No entanto, ainda é interessante e uma experiência de aprendizado experimentar.

Embora eu concorde com a maioria das respostas aqui, acho que vale a pena acrescentar uma nova consideração:Tecnologia Speedstep.

Ao executar um trabalho de thread único com uso intensivo de CPU em um sistema multi-core, no meu caso, um Xeon E5-2430 com 6 núcleos reais (12 com HT) no Windows Server 2012, o trabalho foi distribuído entre todos os 12 núcleos, usando cerca de 8,33% de cada núcleo e nunca provocando um aumento de velocidade.A CPU permaneceu em 1,2 GHz.

Quando defini a afinidade do thread para um núcleo específico, ele usou cerca de 100% desse núcleo, fazendo com que a CPU atingisse o máximo de 2,5 GHz, mais que dobrando o desempenho.

Este é o programa que usei, que apenas faz um loop aumentando uma variável.Quando chamado com -a, definirá a afinidade para o núcleo 1.A parte de afinidade foi baseada em esta postagem.

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

E os resultados:

results

Velocidade do processador, conforme mostrado pelo Gerenciador de tarefas, semelhante ao que o CPU-Z relata:

enter image description here

Você não deveria se preocupar em fazer isso sozinho.Tenho aplicativos .NET multithread em execução em máquinas dual-quad e, não importa como os threads são iniciados, seja por meio do ThreadPool ou manualmente, vejo uma boa distribuição uniforme de trabalho em todos os núcleos.

Definitivamente, você pode fazer isso escrevendo a rotina dentro do seu programa.

No entanto, você não deve tentar fazer isso, pois o sistema operacional é o melhor candidato para gerenciar essas coisas.Quero dizer, o programa em modo de usuário não deve tentar fazer isso.

No entanto, às vezes, isso pode ser feito (para usuários realmente avançados) para obter o balanceamento de carga e até mesmo para descobrir o verdadeiro problema de multi thread e multi core (corrida de dados/coerência de cache...), já que threads diferentes estariam realmente sendo executados em processadores diferentes. .

Dito isto, se ainda quiser conseguir, podemos fazê-lo da seguinte forma.Estou fornecendo a você o pseudocódigo para (sistema operacional Windows), mas eles também podem ser feitos facilmente no 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".

Depois que a rotina acima for chamada, os threads sempre estarão executando da seguinte maneira:

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

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

Para obter mais informações, consulte o manual/MSDN para saber mais sobre esses conceitos.

O local onde cada thread vai geralmente é gerenciado pelo próprio sistema operacional ... então gere 4 threads em um sistema de 4 núcleos e o sistema operacional decidirá em quais núcleos executar cada um, o que geralmente será 1 thread em cada núcleo.

É função do sistema operacional dividir threads em diferentes núcleos, e isso será feito automaticamente quando seus threads estiverem usando muito tempo de CPU.Não se preocupe com isso.Para descobrir quantos núcleos seu usuário possui, tente Environment.ProcessorCount em C#.

você não pode fazer isso, pois apenas o sistema operacional tem privilégios para fazer isso.Se você decidir... então será difícil codificar aplicativos.Porque então você também precisa cuidar da comunicação entre processadores.seções críticas.para cada aplicativo você deve criar seus próprios semáforos ou mutex......para qual sistema operacional fornece uma solução comum fazendo isso sozinho.......

Uma das razões pelas quais você não deveria (como foi dito) tentar alocar esse tipo de coisa sozinho, é que você simplesmente não tem informações suficientes para fazê-lo corretamente, especialmente no futuro com NUMA, etc.

Se você tiver um thread pronto para ser executado e houver um núcleo ocioso, o kernel vai execute seu tópico, não se preocupe.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top