25.10.07

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

Tags: .Net 2.0, Программирование / 5:36 pm , Victor Laskin

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

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

Для того чтобы использовать все ядра процессора необходимо создать многопоточный алгоритм с числом потоков равным числу ядер. Обычно ядра два (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-ми ядерных систем ))

13 Comments »

  1. Сергей Третьяк said,
    October 26, 2007 at 11:10 am

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

  2. Victor Laskin said,
    October 26, 2007 at 2:19 pm

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

    int am = System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity.ToInt32();
                int processorCount = 0;
                while (am != 0)
                {
                    processorCount++;
                    am &= (am - 1);
                }
  3. alexander said,
    November 29, 2007 at 3:53 pm

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

  4. Victor Laskin said,
    November 29, 2007 at 4:02 pm

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

  5. alexander said,
    November 29, 2007 at 5:18 pm

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

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

  6. alexander said,
    November 29, 2007 at 6:30 pm

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

  7. alexander said,
    November 29, 2007 at 6:39 pm

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

  8. Ilya said,
    May 27, 2008 at 6:14 pm

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

  9. Victor Laskin said,
    May 27, 2008 at 6:36 pm

    ThreadStart - это делегат

  10. Бегемот said,
    August 21, 2008 at 5:25 pm

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

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

  11. Victor Laskin said,
    August 21, 2008 at 11:33 pm

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

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

  12. Дмитрий said,
    December 4, 2008 at 3:57 pm

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

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

  13. NARFULS said,
    February 4, 2010 at 8:28 pm

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

Leave a Comment

Or use your OpenID:

Информация для блогоспаммеров - не тратьте свое время, все спам комментарии все равно будут удалены.