Оптимизация кода на С# для многоядерных систем – легко!

Не для кого не секрет, что ближайшее будущее за многоядерными системами. И каждый кодер уже должен быть к этому готов.

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

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

Каждый поток представляет собой объект класса Thread. Во время создания потока необходимо указать точку входа – статический метод класса (Thread1Proc, Thread2Proc). Метод Start запускает поток, а Join ждет его завершения.

using System.Threading;
...

Thread t1 = new Thread(new ThreadStart(Thread1Proc));
Thread t2 = new Thread(new ThreadStart(Thread2Proc));
t1.Start();
t2.Start();

while ((t1.ThreadState != ThreadState.Stopped) && (t2.ThreadState != ThreadState.Stopped))
{
  //Here we could show some info about process
  Application.DoEvents();
  Thread.Sleep(1);
}

t1.Join();
t2.Join();

.....

public static void Thread1Proc()
{
//Do 1th half
}

public static void Thread2Proc()
{
//Do 2nd half
}

Такая оптимизация позволяет поднять скорость выполнения кода в два раза! Единственная проблема, что не всегда можно разбить код на две параллельные части.

Будем ждать 128-ми ядерных систем ))

  • А как программно определить количество ядер?

  • Например, так:


    int am = System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity.ToInt32();
    int processorCount = 0;
    while (am != 0)
    {
    processorCount++;
    am &= (am - 1);
    }

  • alexander

    А вы уверены, что “Для того чтобы использовать все ядра процессора необходимо создать многопоточный алгоритм с числом потоков равным числу ядер.”? Я не разу не слышал, что в .net-е можно распределять потоки между ядрами.

  • В том то и идея, что все прекрасно работает без геморроя. Достаточно равномерно разделить нагрузку между потоками и они сами распределяться по ядрам. Так что нет нужды каждый поток привязывать к конкретному ядру. Более того можно сделать довольно большое число потоков, не равное числу ядер – например 128. И тоже все будет хорошо. Видел такой пример на сайте АМД.

  • alexander

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

    Также замечу, что всё-таки уж совсем играться с системой не следует. И 128 потоков на обыденой машине(я не говорю про сервер, напримр тот же Core2 Duo) это слишком много. Тут нужно не забывать, что потоки-потоками, а вот память тоже нужно экономить несмотря на её дешевизну. А в остальном респект! 🙂

  • alexander

    ДА, с нахождением количества ядер ты явно перестарался 🙂
    Может проще так – Environment.ProcessorCount;
    lol

  • alexander

    Анн, нет… звиняй ошибся это только процеесоры считает. 🙂

  • Ilya

    Интересно, а почему тоже самое не сделать с помощью делегатов?

  • ThreadStart – это делегат

  • Бегемот

    Учите матчасть!
    Даже один поток виртуальная машина .Net(Framework) розкидает по двум ядрам(если он достаточно ресурсоемкий).
    Такчто нет заботы самим туда лезть.

    А в результате того что тут обсуждаеться ничего кроме “гавнокода” (не побоюсь этого слова) не получится

  • Мой опыт показывает, что если никакой оптимизации нету, то стабильно загрузка двуядерного процессора 50%. Может у меня “бракованная” виртуальная машина…

    Насчет красоты кода – да, оптимизация несет в себе необходимость создания чуть более сложных многопоточных структур, но это не значит, что это приведет к коду плохого качества. Кроме того, тут описан лишь подход. Можно, например, использовать BackgroundWorker для рождения дочерних потоков.

  • Дмитрий

    Использовал практически код на 2 ядерной машине, но давал 4 потока так как время выполнения каждого было разным. Действительно повышает производительность и нагружает процессор на 100% вместо ~50 в однопоточном варианте. Это была математика часа этак на два.

    Просьба не утверждать продукты теоретических измышлений без практической проверки.

  • NARFULS

    отлично работает. как раз прога под ето дело нуждалась в ускорении примерно в два раза.
    главное сработало =))
    спасибо

  • Windows каждое ядро видит как отдельный процессор, так что строка
    int count = Environment.ProcessorCount;
    реально сократит количество кода и подсчитает количество ядер.