Заставить Task ‹T› на другое ядро?

Tpl и Plinq автоматически назначают работу потокам (на ядре / с ... {ну, если #threads> #cores, то> 1 поток будет выполняться на том же ядре.}).

Однако, допустим, у меня есть MyMethod1(){..} и MyMethod2(){..}, и мне нужно убедиться (!), Что каждый из них будет работать на другом ядре! (например, интенсивные вычисления)

Ближайшее решение, которое я нашел, - это Plinq's .WithExecutionMode (ParallelExecutionMode.ForceParallelism)

Но это для другой ситуации, когда Plinq может подумать, что лучше делать это последовательно, чем параллельно. Кроме того, я не использую Plinq. У меня есть только 2 метода, которые нужно запускать на другом ядре.

Как мне это сделать ?

p.s. здесь, в SO, был ответ, который предлагал использовать TaskCreationOptions.LongRunning, но это только намек для TaskScheduler, что он должен более агрессивно создавать потоки пула потоков. но эти потоки могут быть на одном ядре. и моя ситуация требовала, чтобы они были в другом ядре.

Спасибо.


person Royi Namir    schedule 30.03.2013    source источник
comment
Я не думаю, что вы можете иметь такой большой контроль над тем, где будет выполняться поток даже в WinAPI, AFAIK ОС решает, на каком ядре будет запускаться поток - ссылка на связанный вопрос   -  person Patryk Ćwiek    schedule 30.03.2013
comment
Если у вашего оборудования есть как минимум два ядра и достаточно шума, чтобы оно не было перегружено готовыми потоками, разве в любом случае может не произойти то, что вам кажется,?   -  person Martin James    schedule 30.03.2013
comment
@MartinJames Я думал, что Windows не будет знать (в начале), что потоки выполняют массовые вычисления - и, следовательно, - может планировать оба потока на одном ядре.   -  person Royi Namir    schedule 30.03.2013
comment
Однако ОС автоматически перемещает потоки между ядрами. stackoverflow.com/questions/1579002/   -  person Matthew Watson    schedule 30.03.2013
comment
@RoyiNamir он не может отправлять более одного потока на ядро. Если есть два свободных ядра и два готовых потока, он отправит по одному потоку на каждое ядро.   -  person Martin James    schedule 30.03.2013
comment
Кроме того, я не понимаю, как он не мог поменять местами потоки на ядрах. Если поток A работает на ядре 1 и что-то блокируется, он больше не работает. Если A снова становится готовым, в то время как поток B уже работает на ядре 1, но ядро ​​2 свободно, вполне разумно, что ОС запускает A на ядре 2. Кроме того, если ОС обнаруживает, что A и B оба работают на двух ' ядер, которые связаны между собой гиперпоточностью, он может решить переместить один поток в полностью независимое ядро ​​для повышения общей производительности.   -  person Martin James    schedule 30.03.2013
comment
@MartinJames. Почему вы думаете, что я думал, что он может отправлять более одного потока на ядро? каждое ядро ​​- каждый раз - будет иметь свой временной интервал на ядре. но мой вопрос к Джеффри заключался в том, были ли потоки a и b запланированы на ядро ​​# 0, могут ли Windows определить, что b также вычисляет интенсивный поток, и переместить его в ядро ​​# 1 ....   -  person Royi Namir    schedule 30.03.2013
comment
Не понимаю, как он мог знать, поэтому ему все равно. Я не понимаю, что «поток a и b был запланирован для ядра № 0» - если поток становится готовым, и одно ядро ​​свободно, оно запустит его на этом ядре, если ядро, на котором оно ранее выполнялось, занято другим. нить.   -  person Martin James    schedule 30.03.2013
comment
@MartinJames Мы согласны с тем, что если есть 2 ядра и много потоков, где 2 из них являются моими интенсивными вычислительными потоками - это займет меньше времени, если каждый поток (мои потоки) будет работать (когда у него есть свои отрезок времени) на разных ядрах .... верно? это мой настоящий вопрос. вот почему TPL на самом деле пытается сделать (всякий раз, когда может)   -  person Royi Namir    schedule 31.03.2013


Ответы (1)


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

Первое, что вам нужно сделать, это убедиться, что ваши два Tasks выполняются в разных управляемых потоках. Поскольку это попытка установить ручной контроль над чем-то, что обрабатывается фреймворком, чтобы быть уверенным, что это именно тот случай, вам нужно будет написать свой собственный TaskScheduler. Однако реально это можно сделать, указав флаг TaskCreationOptions.LongRunning. По крайней мере, на текущей настольной среде CLR это всегда будет создавать новый поток. Но это всего лишь подсказка с точки зрения API.

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

Затем вам нужно получить собственный поток, соответствующий текущему управляемому потоку. В каждом методе после вызова BeginThreadAffinity получите собственный поток, вызвав GetCurrentThreadId через p / invoke.

Теперь вы можете сделать все остальное как в родной, так и в управляемой среде, но я предполагаю, что вы захотите сделать это в .NET. В этом случае получите объект ProcessThread, который соответствует собственному потоку, и вы можете установить сходство процессора или идеальный процессор оттуда:

Thread.BeginThreadAffinity();
int threadId = GetCurrentThreadId();
Process proc = Process.GetCurrentProcess();
ProcessThread procThread = proc.Threads.Cast<ProcessThread>().Single(
    pt => pt.Id == threadId
);
procThread.ProcessorAffinity = new IntPtr(0x01);
//
// work
//
procThread.ProcessorAffinity = new IntPtr(0xFFFF);
Thread.EndThreadAffinity()
person Jacob    schedule 30.03.2013