Немного про Parallel.For

by Alexei 25. April 2010 13:16

Библиотека Parallel Extensions, изначально разработанная как исследовательский проект вошла в состав .Net 4.0. Целью её создания было упростить программирование под многоядерные архитектуры.

Данная библиотека содержит множество средств для автоматического распараллеливания кода. Остановимся поподробнее на методе Parallel.For.

Это статический метод с 3-мя агрументами. Библиотека содержит сложные алгоритмы динамического распределения работы и автоматически приспосабливается к конкретной машине. В то же время примитивы библиотеки позволяют только указать на возможный параллелизм, но не гарантируют параллельного исполнения. Например, на однопроцессорной машине параллельный цикл будет выполнен последовательно. Скорость его выполнения будет очень близка к скорости выполнения последовательного кода. Однако на двухъядерном компьютере библиотека задействует два потока для параллельного выполнения цикла. Можно сразу включить инструкции параллельного выполнения в код, тогда при наличии нескольких процессоров приложение автоматически начнет их использовать. При этом код будет быстро работать и на однопроцессорных машинах.

Для примера рассмотрим классическую задачу из теории чисел - т.н. гипотезу Колатца. Она заключается в слелующем:

Рассмотрим следующий алгоритм генерации последовательности чисел. Начнем с целого числа n. Если n четно, то разделим n на 2.Если n нечетно, то умножим на 3 и добавим 1. Будем повторять этот процесс с новыми полученным n, пока n не станет равным 1. Например, для n = 22 будет сгенерирована следующая последовательность чисел:
22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1

Длина этой последовательности равна 16. До сих пор не доказано, что для любого числа такая последовательность закончится единицей.

Попробуем найти число в пределах от 1 до 1000000, которое генерирует последовательность наибольшей длины:

using System;
using System.Collections.Generic;
using System.Text;

namespace _14
{
    class Program
    {
        static long Max;
        static long MaxNum;
        
        static void calc(long a)
        {
            long ans = 1;
            long tmp_a = a;
            
            while (a != 1)
            {
                if (a % 2 == 0)
                    a /= 2;
                else
                    a = 3 * a + 1;
                ans++;
            }
            
            if (Max < ans)
            {
                MaxNum = tmp_a;
                Max = ans;
            }
        }

        static void Main(string[] args)
        {
            Max = -1;
            MaxNum = 0;
            
            DateTime startTime = DateTime.Now;
            for (int i = 1; i <= 1000000; i++)
                calc(i);
            
            DateTime stopTime = DateTime.Now;

            TimeSpan elapsed = stopTime - startTime;
            Console.WriteLine("elapsed seconds: {0}", elapsed.Seconds);
            Console.WriteLine("elapsed milliseconds: {0}", elapsed.Milliseconds);
            Console.WriteLine(MaxNum);
        }
    }
}

Вышеприведённый код использует для вычислений тип long чтобы избежать переполнения(элементы последовательности могут быть гораздо больше 1000000).

Время работы на моей машине с процессором Core 2 Duo 2,6 Ггц - 3 секунды и 232 миллисекунды.

Заметим, что вычисление длины последовательности для конкретного числа не зависит ни от каких других вычислений, поэтому эту задачу можно эффективно распараллелить. Для этого воспользуемся методом Parallel.For:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace _14
{
    class Program
    {
        static long Max;
        static long MaxNum;
        
        static void calc(long a)
        {
            long ans = 1;
            long tmp_a = a;
            
            while (a != 1)
            {
                if (a % 2 == 0)
                    a /= 2;
                else
                    a = 3 * a + 1;
                ans++;
            }
            
            if (Max < ans)
            {
                MaxNum = tmp_a;
                Max = ans;
            }
        }

        static void Main(string[] args)
        {
            Max = -1;
            MaxNum = 0;
            
            DateTime startTime = DateTime.Now;
            System.Threading.Tasks.Parallel.For(1, 1000000, i =>
            {
                calc(i);
            }
            );

            DateTime stopTime = DateTime.Now;

            TimeSpan elapsed = stopTime - startTime;
            Console.WriteLine("elapsed seconds: {0}", elapsed.Seconds);
            Console.WriteLine("elapsed milliseconds: {0}", elapsed.Milliseconds);
            Console.WriteLine(MaxNum);
            Console.ReadKey();
        }
    }
}

Понадобилось немного изменить всего одну строчку и время работы уменьшилось до 1 секунды и 738 миллисекунд, т.е. примерно в 2 раза.

Tags: ,

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading



Powered by BlogEngine.NET 1.6.1.0
Theme by Extensive SEO