Linq.AsParallel()
От: Аноним  
Дата: 06.05.13 17:02
Оценка:
Привет

Кто может объяснить такой факт.
Запускаем данный код и видим,
что первый вызов K1 всегда дает, что время работы Sum для Array в разы дольше чем Queue,
а вот последующие вызовы уже Array работает быстрей.
Я конечно понял, что второй и следующие разы K1 уже скомпилирована, но почему в первый раз Array занимеает намного дольше, чем Queue. И что самое интересное, если их поменять месстами, то Queue уже будет занимать дольше времени?


  class Program
    {
        static void Main(string[] args)
        {
            K1(50000);
            K1(50000);
            K1(50000);
            Console.ReadKey();
        }
        static void K1(int N)
        {
            var count = N;
            Queue<Country> queue = new Queue<Country>(count);
            for (int i = 0; i < count; i++)
                queue.Enqueue(new Country() { Id = i, Name = i.ToString() });

            Country[] arr = new Country[count];
            for (int i = 0; i < count; i++)
                arr[i] = new Country() { Id = i, Name = i.ToString() };

            Stopwatch sw = new Stopwatch();
            sw.Start();
            Int64 sum = arr.AsParallel().Sum(p => p.Id);
            sw.Stop();

            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            Int64 sum2 = queue.AsParallel().Sum(p => p.Id);
            sw2.Stop();


            Console.WriteLine("ARRAY " + sw.ElapsedMilliseconds);
            Console.WriteLine("queue " + sw2.ElapsedMilliseconds);
        }
    }
    class Country
    {
        public int Id;
        public string Name;
    }
Re: Linq.AsParallel()
От: Аноним  
Дата: 09.05.13 05:31
Оценка:
Здравствуйте, Аноним, Вы писали:


Неужели никто не знает в чем причина такой работы?

А>Привет


А>Кто может объяснить такой факт.

А>Запускаем данный код и видим,
А>что первый вызов K1 всегда дает, что время работы Sum для Array в разы дольше чем Queue,
А>а вот последующие вызовы уже Array работает быстрей.
А>Я конечно понял, что второй и следующие разы K1 уже скомпилирована, но почему в первый раз Array занимеает намного дольше, чем Queue. И что самое интересное, если их поменять месстами, то Queue уже будет занимать дольше времени?

А>

А>  class Program
А>    {
А>        static void Main(string[] args)
А>        {
А>            K1(50000);
А>            K1(50000);
А>            K1(50000);
А>            Console.ReadKey();
А>        }
А>        static void K1(int N)
А>        {
А>            var count = N;
А>            Queue<Country> queue = new Queue<Country>(count);
А>            for (int i = 0; i < count; i++)
А>                queue.Enqueue(new Country() { Id = i, Name = i.ToString() });

А>            Country[] arr = new Country[count];
А>            for (int i = 0; i < count; i++)
А>                arr[i] = new Country() { Id = i, Name = i.ToString() };

А>            Stopwatch sw = new Stopwatch();
А>            sw.Start();
А>            Int64 sum = arr.AsParallel().Sum(p => p.Id);
А>            sw.Stop();

А>            Stopwatch sw2 = new Stopwatch();
А>            sw2.Start();
А>            Int64 sum2 = queue.AsParallel().Sum(p => p.Id);
А>            sw2.Stop();


А>            Console.WriteLine("ARRAY " + sw.ElapsedMilliseconds);
А>            Console.WriteLine("queue " + sw2.ElapsedMilliseconds);
А>        }
А>    }
А>    class Country
А>    {
А>        public int Id;
А>        public string Name;
А>    }
А>
Re: Linq.AsParallel()
От: MT-Wizard Украина  
Дата: 09.05.13 06:10
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Привет


А>Кто может объяснить такой факт.


Стартуют потоки в пуле?
А ти, москалику, вже приїхав (с)
Re[2]: Linq.AsParallel()
От: Аноним  
Дата: 09.05.13 07:40
Оценка:
Здравствуйте, MT-Wizard, Вы писали:

MW>Здравствуйте, Аноним, Вы писали:


А>>Привет


А>>Кто может объяснить такой факт.


MW>Стартуют потоки в пуле?


Весь код я выложил


class Program
    {
        static void Main(string[] args)
        {
            K1(50000);
            K1(50000);
            K1(50000);
            Console.ReadKey();
        }
        static void K1(int N)
        {
            var count = N;
            Queue<Country> queue = new Queue<Country>(count);
            for (int i = 0; i < count; i++)
                queue.Enqueue(new Country() { Id = i, Name = i.ToString() });

            Country[] arr = new Country[count];
            for (int i = 0; i < count; i++)
                arr[i] = new Country() { Id = i, Name = i.ToString() };

            Stopwatch sw = new Stopwatch();
            sw.Start();
            Int64 sum = arr.AsParallel().Sum(p => p.Id);
            sw.Stop();

            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            Int64 sum2 = queue.AsParallel().Sum(p => p.Id);
            sw2.Stop();


            Console.WriteLine("ARRAY " + sw.ElapsedMilliseconds);
            Console.WriteLine("queue " + sw2.ElapsedMilliseconds);
        }
    }
    class Country
    {
        public int Id;
        public string Name;
    }
Re: Linq.AsParallel()
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 10.05.13 08:21
Оценка: 3 (1) +1
Здравствуйте, Аноним, Вы писали:

А>Привет



А>Кто может объяснить такой факт.

А>Запускаем данный код и видим,
А>что первый вызов K1 всегда дает, что время работы Sum для Array в разы дольше чем Queue,
А>а вот последующие вызовы уже Array работает быстрей.
А>Я конечно понял, что второй и следующие разы K1 уже скомпилирована, но почему в первый раз Array занимеает намного дольше, чем Queue. И что самое интересное, если их поменять месстами, то Queue уже будет занимать дольше времени?

Ну, так тестировать производительность нельзя, особенно в управляемых средах. Вначале нужно делать "прогревочный" прогон, а затем уже что-либо мерять. В данном случае видится такое объяснение:

1. JIT-инг. AsParallel построен на куче разной фигни, которая при первом запуске будет компилиться.
Так что дело не в том, что "второй и следующие разы K1 уже скомпилирована", а в том, что в момент первого вызова K1 при arr.AsParallel() происходит JIT-инг, а при вызове queue.AsParallel() уже все готово.

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

3. Кэширование делегатов.
Мелочь, но все же.
// Вот этот код:
Int64 sum = arr.AsParallel().Sum(p => p.Id);

// Выворачивается так:
// Статическое поле...
static System.Func<Country, int> __CachedAnonymousMethodDelegate4;
// и ленивое создание
if (__CachedAnonymousMethodDelegate4 == null)
    __CachedAnonymousMethodDelegate4 = new System.Func<Country, int>(b__2);


Поэтому при первом обращении к Sum(p => p.Id) происходит дополнительная аллокация. Она не повлияет на результат в данном случае, это просто повод показать, что "под колпаком" может происходить много чего

В общем, для того, чтобы указанной проблемы не было при тестировании производительности нужно всегда делать прогревочный прогон, а уже потом что-то измерять. Ниже измененный пример, который ведет себя предсказуемым образом (Array всегда быстре Queue в пару раз):

static Queue<Country> _countriesQueue; 
static Country[] _countriesArray;

static void Create(int count)
{
    _countriesQueue = new Queue<Country>(count);
    for (int i = 0; i < count; i++)
        _countriesQueue.Enqueue(new Country() { Id = i, Name = i.ToString() });

    _countriesArray = new Country[count];
    for (int i = 0; i < count; i++)
        _countriesArray[i] = new Country() { Id = i, Name = i.ToString() };
}

static long SumQueue()
{
    return _countriesQueue.AsParallel().Sum(p => p.Id);
}

static long SumArray()
{
    return _countriesArray.AsParallel().Sum(p => p.Id);
}

static void CompareQueueAndArray(int count, int iterations)
{
    Create(count);
            
    // "Прогреваем" тестируемый код. 
    // В результате будут подгружены все нужные сборки, закешированы
    // все делегаты и т.п.
    SumQueue();
    SumArray();

    // Теперь мы можем менять следующие блоки местами, но результаты 
    // от этого изменяться не будут!
    var arraySw = Stopwatch.StartNew();
    for (int i = 0; i < iterations; i++)
    {
        SumArray();
    }
    arraySw.Stop();

    var queueSw = Stopwatch.StartNew();
    for(int i = 0 ; i < iterations; i++)
    {
        SumQueue();
    }
    queueSw.Stop();

    Console.WriteLine("ARRAY: {0}ms. Count: {1}, Iterations: {2}", 
        arraySw.ElapsedMilliseconds, count, iterations);
    Console.WriteLine("QUEUE: {0}ms. Count: {1}, Iterations: {2}", 
        queueSw.ElapsedMilliseconds, count, iterations);
}
        
static void Main(string[] args)
{
    CompareQueueAndArray(50000, 3);
            
    Console.ReadKey();
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.