быстрый ToEnumerable
От: tyomchick Россия  
Дата: 23.04.15 11:43
Оценка:
Часто методы принимают IEnumerable, а передавать хочется всего один элемент.

Решил написать хелпер:

 public static IEnumerable<T> ToEnumerable<T>(this T value)
 {
   return new[] { value };
 }


Наивным тестами выяснил, что он быстрее чем
yield return value;


(хоть и уступает по зажору памяти), и экономичнее по памяти чем:

return Enumerable.Repeat(value, 1);

(при почти идентичной производительности).

Есть ли более быстрый способ?

PS:
На счет памяти есть сомнения. Мерил через:
GC.GetTotalMemory(false)

, если мерить через
Environment.WorkingSet

, то массив и по памяти самый экономичный.
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Отредактировано 23.04.2015 11:46 tyomchick . Предыдущая версия .
Re: быстрый ToEnumerable
От: xy012111  
Дата: 23.04.15 12:32
Оценка:
Здравствуйте, tyomchick, Вы писали:

T>Часто методы принимают IEnumerable, а передавать хочется всего один элемент.

T>Решил написать хелпер:
T>Наивным тестами выяснил, что он быстрее чем

T>Есть ли более быстрый способ?

Ну раз кода тестов вы не привели, то попробуйте пожалуйста сами:
using System;
using System.Collections;
using System.Collections.Generic;

static class Program
{
  static void Main() {
  }

  public static IEnumerable<T> ToEnumerable<T>(T item) {
    return new Single<T>(item);
  }
}

internal sealed class Single<T> : IEnumerable<T>
{
  private readonly T value;

  public Single(T value) {
    this.value = value;
  }

  public IEnumerator<T> GetEnumerator() {
    return new Enumerator(this);
  }

  IEnumerator IEnumerable.GetEnumerator() {
    return GetEnumerator();
  }

  private sealed class Enumerator : IEnumerator<T>
  {
    private readonly Single<T> owner;

    public Enumerator(Single<T> owner) {
      this.owner = owner;
    }

    private bool IsEnumerated { get; set; }

    public T Current {
      get { return IsEnumerated ? owner.value : default(T); }
    }

    // Does not correct (InvalidOperationException does not thrown), but usable.
    object IEnumerator.Current {
      get { return Current; }
    }

    public bool MoveNext() {
      if(IsEnumerated) {
        return false;
      } else {
        IsEnumerated = true;
        return true;
      }
    }

    public void Reset() {
      IsEnumerated = false;
    }

    public void Dispose() { }
  }
}
Re: быстрый ToEnumerable
От: hardcase Пират http://nemerle.org
Дата: 23.04.15 12:37
Оценка:
Здравствуйте, tyomchick, Вы писали:

T>Часто методы принимают IEnumerable, а передавать хочется всего один элемент.


Массив — то что надо.
http://nemerle.org/Banners/?t=Developer!&g=dark /* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: быстрый ToEnumerable
От: tyomchick Россия  
Дата: 23.04.15 15:20
Оценка:
Здравствуйте, xy012111, Вы писали:

X>Здравствуйте, tyomchick, Вы писали:


T>>Часто методы принимают IEnumerable, а передавать хочется всего один элемент.

T>>Решил написать хелпер:
T>>Наивным тестами выяснил, что он быстрее чем
X>…
T>>Есть ли более быстрый способ?

X>Ну раз кода тестов вы не привели, то попробуйте пожалуйста сами:


Почему то этот ручной вариант самый медленный.

Код кустарного теста:



 class Program
    {        
        private const int ITERATION_COUNT = 10000000;

        static void Main(string[] args)
        {
            Console.WriteLine("Тест запущен");
            var memoryGC = GC.GetTotalMemory(false);
            var memoryWS = Environment.WorkingSet;
            var start = Environment.TickCount;
            //Test0();
            //Test1();
            //Test2();
            //Test3();
            Test4();
            Console.WriteLine("Time: {0}, Memory GC: {1}, Memory WS: {2}", Environment.TickCount - start,
                              GC.GetTotalMemory(false) - memoryGC, Environment.WorkingSet - memoryWS);
            Console.ReadLine();
        }

        private static int Test0()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for (var i = 0; i < ITERATION_COUNT; i++)
                value += value;
            return value;
        }

        private static int Test1()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for (var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable1().Sum();
            return value;
        }

        private static int Test2()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for (var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable2().Sum();
            return value;
        }

        private static int Test3()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for (var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable3().Sum();
            return value;
        }                

        private static int Test4()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for (var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable4().Sum();
            return value;
        }                
    }

    public static class Helper
    {
        public static IEnumerable<T> AsEnumerable1<T>(this T value)
        {
            yield return value;  
        }        

        public static IEnumerable<T> AsEnumerable2<T>(this T value)
        {
            return Enumerable.Repeat(value, 1);            
        }        

        public static IEnumerable<T> AsEnumerable3<T>(this T value)
        {
            return new[] { value };
        }        

        public static IEnumerable<T> AsEnumerable4<T>(this T value)
        {
            return new Single<T>(value);
        }        

    }


Результаты:

0: Time: 15, Memory GC: 8192, Memory WS: 45056
1: Time: 562, Memory GC: 1138544, Memory WS: 2449408
2: Time: 437, Memory GC: 1605488, Memory WS: 2695168
3: Time: 421, Memory GC: 1451296, Memory WS: 2285568
4: Time: 640, Memory GC: 1139284, Memory WS: 2519040
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Re[2]: быстрый ToEnumerable
От: hi_octane Беларусь  
Дата: 23.04.15 15:47
Оценка:
У тебя два класса. Один элемент структурой оборачивать надо.
Re[3]: быстрый ToEnumerable
От: tyomchick Россия  
Дата: 23.04.15 17:10
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>У тебя два класса. Один элемент структурой оборачивать надо.

Кстати, если сочинить такой сам себе итератор:

 internal struct Single<T> : IEnumerable<T>, IEnumerator<T>
    {
        private readonly T _value;
        private bool _isEnumerated ;

        public Single(T value)
        {
            _value = value;           
            _isEnumerated = false;
        }

        public IEnumerator<T> GetEnumerator()
        {
            Reset();
            return this;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            Reset();
            return this;
        }
              
        public T Current
        {
            get { return _isEnumerated ? _value : default(T); }
        }

        
        object IEnumerator.Current
        {
            get { return Current; }
        }

        public bool MoveNext()
        {
            if (_isEnumerated)
            {
                return false;
            }
            else
            {
                _isEnumerated = true;
                return true;
            }
        }

        public void Reset()
        {
            _isEnumerated = false;
        }

        public void Dispose() { }        
    }


то я получаю такой результат:

Time: 608, Memory GC: 1301792, Memory WS: 2473984

Т.е. большая нагрузка на кучу сохраняется.
Получается объект всё равно в кучке создается или просто при приведении к интерфейсу всё равно боксинг происходит?
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Re[4]: быстрый ToEnumerable
От: hi_octane Беларусь  
Дата: 23.04.15 18:27
Оценка: 2 (1)
T>Т.е. большая нагрузка на кучу сохраняется.
T>Получается объект всё равно в кучке создается или просто при приведении к интерфейсу всё равно боксинг происходит?

В этом случае решарпер показал аж 3 боксинга. После того как я изменил код на использование stopwatch, сделал несколько проходов для устаканивания JIT, добавил GC.Collect, а твой сам-себе итератор поправил на класс чтобы ушли боксинги, получилось примерно так.

Итерация 3
Тест 0 запущен
Time: 3, Memory GC: 0, Memory WS: 0
Тест 1 запущен
Time: 1195, Memory GC: 483328, Memory WS: 0
Тест 2 запущен
Time: 782, Memory GC: 786432, Memory WS: 0
Тест 3 запущен
Time: 551, Memory GC: 353824, Memory WS: 8192
Тест 4 запущен
Time: 388, Memory GC: 920240, Memory WS: 4096


Т.е. сам-себе итератор сделанный классом порвал массив. Код под катом.

  Скрытый текст
        private const int ITERATION_COUNT = 10000000;

        static Stopwatch stopwatch = new Stopwatch();

        static void Main(string[] args)
        {
            var tests = new Func<int>[] { Test0, Test1, Test2, Test3, Test4};

            for(var i = 0; i < 4; i++)
            {
                Console.WriteLine("Итерация {0}", i);

                for(var j=0; j<tests.Length;j++)
                {
                    RunTest(j, tests[j]);

                    GC.Collect(0, GCCollectionMode.Forced);
                    Thread.Sleep(1000);
                }                
            }
                        
            //Test0();
            //Test1();
            //Test2();
            //Test3();
            //Test4();
            
        }
        
        static void RunTest(int num, Func<int> test)
        {
            Console.WriteLine("Тест {0} запущен", num);
            var memoryGC = GC.GetTotalMemory(false);
            var memoryWS = Environment.WorkingSet;

            stopwatch.Reset();
            stopwatch.Start();
            test();
            stopwatch.Stop();

            Console.WriteLine("Time: {0}, Memory GC: {1}, Memory WS: {2}", stopwatch.ElapsedMilliseconds,
                              GC.GetTotalMemory(false) - memoryGC, Environment.WorkingSet - memoryWS);
            
        }


        private static int Test0()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for(var i = 0; i < ITERATION_COUNT; i++)
                value += value;
            return value;
        }

        private static int Test1()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for(var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable1().Sum();
            return value;
        }

        private static int Test2()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for(var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable2().Sum();
            return value;
        }

        private static int Test3()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for(var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable3().Sum();
            return value;
        }

        private static int Test4()
        {
            var value = new Random(DateTime.Now.Millisecond).Next();
            for(var i = 0; i < ITERATION_COUNT; i++)
                value += value.AsEnumerable4().Sum();
            return value;
        }
    }

    public static class Helper
    {
        public static IEnumerable<T> AsEnumerable1<T>(this T value)
        {
            yield return value;
        }

        public static IEnumerable<T> AsEnumerable2<T>(this T value)
        {
            return Enumerable.Repeat(value, 1);
        }

        public static IEnumerable<T> AsEnumerable3<T>(this T value)
        {
            return new[] { value };
        }

        public static IEnumerable<T> AsEnumerable4<T>(this T value)
        {
            return new Single<T>(value);
        }

    }
Re[4]: быстрый ToEnumerable
От: hi_octane Беларусь  
Дата: 24.04.15 07:31
Оценка: +4
_>>У тебя два класса. Один элемент структурой оборачивать надо.
T>Кстати, если сочинить такой сам себе итератор:

Кстати у этого итератора граница применимости — строго однопоточные и однопроходные алгоритмы. Многие стандартные алгоритмы на нём сломаются, например SequenceEqual:

var x = new Single<int>(10);

var t1 = x;
var t2 = x;

Assert.IsTrue(t1.SequenceEqual(t2)); <- упс


Такие грабли по проекту ради миллисекунд я бы не раскладывал.
Re[5]: быстрый ToEnumerable
От: tyomchick Россия  
Дата: 26.04.15 11:07
Оценка:
Здравствуйте, hi_octane, Вы писали:

T>>Т.е. большая нагрузка на кучу сохраняется.

T>>Получается объект всё равно в кучке создается или просто при приведении к интерфейсу всё равно боксинг происходит?

_>В этом случае решарпер показал аж 3 боксинга. После того как я изменил код на использование stopwatch, сделал несколько проходов для устаканивания JIT, добавил GC.Collect, а твой сам-себе итератор поправил на класс чтобы ушли боксинги, получилось примерно так.


Ага, спасибо за нормальный тестер.

Правда на моей рабочей машине результат не стабильный. Судя по всему дело в том, что мой комп беден оперативкой и в ходе теста запускается сборщик мусора. На это указывают и странные показатели по памяти. И тест производительности по сути мало что показывает, т.к. портится запуском сборщика.
Я заменил в этом тесте бесполезный показатель Memory WS, на CollectionCount(0):

  код
static void RunTest(int num, Func<int> test)
{
    Console.WriteLine("Тест {0} запущен", num);
        var memoryGC = GC.GetTotalMemory(false);
        var collectionCount = GC.CollectionCount(0);            

        stopwatch.Reset();
        stopwatch.Start();
        test();
        stopwatch.Stop();

        Console.WriteLine("Time: {0}, Memory GC: {1}, Collection count: {2}", stopwatch.ElapsedMilliseconds,
                          GC.GetTotalMemory(false) - memoryGC, GC.CollectionCount(0) - collectionCount);
}

Получилось такое:

Тест 0 запущен
Time: 17, Memory GC: 0, Collection count: 0
Тест 1 запущен
Time: 414, Memory GC: 1073152, Collection count: 133
Тест 2 запущен
Time: 447, Memory GC: 1531904, Collection count: 190
Тест 3 запущен
Time: 448, Memory GC: 1385824, Collection count: 171
Тест 4 запущен
Time: 331, Memory GC: 614400, Collection count: 76


Уменьшил число итераций в 200 раз. Запускал с приоритетом "Real Time", для предотвращения переключения контекста процессора. Результат в тиках (stopwatch.ElapsedTicks):

Тест 0 запущен
Time: 305, Memory GC: 0, Collection count: 0
Тест 1 запущен
Time: 15790, Memory GC: 1392640, Collection count: 0
Тест 2 запущен
Time: 17193, Memory GC: 1998848, Collection count: 0
Тест 3 запущен
Time: 17490, Memory GC: 1794048, Collection count: 0
Тест 4 запущен
Time: 12304, Memory GC: 794624, Collection count: 0


Получается из реальный итераторов (к коим "сам-себе", как ты справедливо заметил не относится) самый быстрый yield?
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Отредактировано 26.04.2015 11:08 tyomchick . Предыдущая версия .
Re[5]: быстрый ToEnumerable
От: tyomchick Россия  
Дата: 26.04.15 11:10
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>>>У тебя два класса. Один элемент структурой оборачивать надо.

T>>Кстати, если сочинить такой сам себе итератор:

_>Кстати у этого итератора граница применимости — строго однопоточные и однопроходные алгоритмы.


Да это понятно, я собственно не планировал. Это был экспериент в наивной попытке сделать итератор на стеке.
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Re[4]: быстрый ToEnumerable
От: Sinclair Россия http://corp.ingrammicro.com/Solutions/Cloud.aspx
Дата: 27.04.15 08:08
Оценка:
Здравствуйте, tyomchick, Вы писали:
T>то я получаю такой результат:

T>Time: 608, Memory GC: 1301792, Memory WS: 2473984


T>Т.е. большая нагрузка на кучу сохраняется.

T>Получается объект всё равно в кучке создается или просто при приведении к интерфейсу всё равно боксинг происходит?
А если — такой:
internal struct SingleEnumerator<T>
{
        private readonly T _value;
        private bool _isEnumerated ;

        public Single(T value)
        {
            _value = value;           
            _isEnumerated = false;
        }
        public T Current
        {
            get { return _isEnumerated ? _value : default(T); }
        }

        object IEnumerator.Current
        {
            get { return Current; }
        }

        public bool MoveNext()
        {
            if (_isEnumerated)
            {
                return false;
            }
            else
            {
                _isEnumerated = true;
                return true;
            }
        }

        public void Reset()
        {
            _isEnumerated = false;
        }
        public void Dispose() { }        
}
internal struct SingleEnumerable<T> : IEnumerable<T>, IEnumerator<T>
{
        private readonly T _value;
        public SingleEnumerable(T value)
        {
            _value = value;           
        }

    public SingleEnumerator<T> GetEnumerator()
        {
            return new SingleEnumerator<T>(_value);
        }
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
http://rsdn.org/File/5743/rsdnaddict.GIF
Re[5]: быстрый ToEnumerable
От: xy012111  
Дата: 27.04.15 09:48
Оценка:
Здравствуйте, Sinclair, Вы писали:

T>>то я получаю такой результат:

T>>Time: 608, Memory GC: 1301792, Memory WS: 2473984
T>>Т.е. большая нагрузка на кучу сохраняется.
T>>Получается объект всё равно в кучке создается или просто при приведении к интерфейсу всё равно боксинг происходит?
S>А если — такой:
S>internal struct SingleEnumerator<T>


Если с помощью этой структуры планируется ускорить изначальный метод
public static IEnumerable<T> ToEnumerable<T>(this T value)
{
   return new[] { value };
}

то боксинг, видится, всё съест. Если поменять сигнаруту на структуру, то конечно будет здорово. Но, кажется, не очень полезно. Или нет?

А это вот опечатка?
internal struct SingleEnumerable<T> : IEnumerable<T>, IEnumerator<T>

Если нет, то код вроде как даже не компильнётся.
Re[6]: быстрый ToEnumerable
От: samius Россия http://sams-tricks.blogspot.com
Дата: 27.04.15 10:13
Оценка:
Здравствуйте, xy012111, Вы писали:

X>Здравствуйте, Sinclair, Вы писали:


X>Если с помощью этой структуры планируется ускорить изначальный метод

X>
X>public static IEnumerable<T> ToEnumerable<T>(this T value)
X>{
X>   return new[] { value };
X>}
X>

X>то боксинг, видится, всё съест. Если поменять сигнаруту на структуру, то конечно будет здорово. Но, кажется, не очень полезно. Или нет?
Этот метод не ускорится, но он и не будет вызываться в цикле foreach. Будет вызываться метод, возвращающий структуру.
public SingleEnumerator<T> GetEnumerator()

X>А это вот опечатка?

X>
X>internal struct SingleEnumerable<T> : IEnumerable<T>, IEnumerator<T>
X>

X>Если нет, то код вроде как даже не компильнётся.
А вроде что помешает?
Re[6]: быстрый ToEnumerable
От: Sinclair Россия http://corp.ingrammicro.com/Solutions/Cloud.aspx
Дата: 27.04.15 10:32
Оценка:
Здравствуйте, xy012111, Вы писали:

X>Если с помощью этой структуры планируется ускорить изначальный метод

X>
X>public static IEnumerable<T> ToEnumerable<T>(this T value)
X>{
X>   return new[] { value };
X>}
X>

X>то боксинг, видится, всё съест. Если поменять сигнаруту на структуру, то конечно будет здорово. Но, кажется, не очень полезно. Или нет?
Конечно же имеется в виду поменять сигнатуру на структуру.
Полезность зависит от того, в каком сценарии предполагается использовать сие счастие.
Если в сценарии, где требуется интерфейс — то да, разницы не будет; сплошной боксинг.
А если напрямую в foreach, то компилятор должен убрать боксинг, а джит — всё заинлайнить, выкинуть конструкторы, и оставить только собственно вызов.
Понятное дело, что в человеконаписанном коде всё то же самое можно сделать руками?
foreach(var i in 42.ToEnumerable())
  Console.WriteLine(i);


Сие эквивалентно просто Console.WriteLine(42).

А вот если код автогенерится, то ещё есть шансы улучшить поведение.
X>А это вот опечатка?
X>
X>internal struct SingleEnumerable<T> : IEnumerable<T>, IEnumerator<T>
X>

X>Если нет, то код вроде как даже не компильнётся.
Да, конечно же опечатка. Писалось из головы.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
http://rsdn.org/File/5743/rsdnaddict.GIF
Re: быстрый ToEnumerable
От: xy012111  
Дата: 27.04.15 12:41
Оценка:
Здравствуйте, Sinclair, Вы писали:

X>>Если с помощью этой структуры планируется ускорить изначальный метод


X>>то боксинг, видится, всё съест. Если поменять сигнаруту на структуру, то конечно будет здорово. Но, кажется, не очень полезно. Или нет?
S>Конечно же имеется в виду поменять сигнатуру на структуру.
S>Полезность зависит от того, в каком сценарии предполагается использовать сие счастие.
S>Если в сценарии, где требуется интерфейс — то да, разницы не будет; сплошной боксинг.
S>А если напрямую в foreach, то компилятор должен убрать боксинг, а джит — всё заинлайнить, выкинуть конструкторы, и оставить только собственно вызов.

S>А вот если код автогенерится, то ещё есть шансы улучшить поведение.

Автогенерить такой неэффективный код — ИМХО проще в генерилке иф сделать на случай одного элемента, чем такие артефакты. Тем более, если от сгенерированного кода ожидается эффективная числодробилка (а иначе и мериться не стоит). Очень-очень узкая область применения. Но да, так будет максимально эффективно (не мерил).

В то время как объединять такой IEnumerable<> (из одного элемента) с другими IEnumerable<> ну мне вот приходится постоянно и тут структура не помогает. Здесь можно схитрить и иметь Single<> классом со структурой-энумератором и сменить сигнатуру метода на Single<>. Тогда и объединение будет дешёвым и итерация (в случае кодогенерации или вообще) не плохой.
Re[7]: быстрый ToEnumerable
От: xy012111  
Дата: 27.04.15 12:42
Оценка:
Здравствуйте, samius, Вы писали:

X>>А это вот опечатка?

X>>internal struct SingleEnumerable<T> : IEnumerable<T>, IEnumerator<T>

X>>Если нет, то код вроде как даже не компильнётся.
S>А вроде что помешает?

IEnumerator<T> не реализован же. Вместо "IEnumerator<T>" должно было быть по всей видимости "IEnumerable".
Re[8]: быстрый ToEnumerable
От: samius Россия http://sams-tricks.blogspot.com
Дата: 27.04.15 13:19
Оценка:
Здравствуйте, xy012111, Вы писали:

X>Здравствуйте, samius, Вы писали:


X>>>Если нет, то код вроде как даже не компильнётся.

S>>А вроде что помешает?

X>IEnumerator<T> не реализован же. Вместо "IEnumerator<T>" должно было быть по всей видимости "IEnumerable".

Да, теперь вижу что он реализован в SingleEnumerator<T>
Re[3]: быстрый ToEnumerable
От: xy012111  
Дата: 28.04.15 11:58
Оценка:
Здравствуйте, tyomchick, Вы писали:

X>>Ну раз кода тестов вы не привели, то попробуйте пожалуйста сами:

T>Почему то этот ручной вариант самый медленный.

Я взял код тестов рядом тут и запустил. Получилось быстрее массива

Немного поменял Single<>, добавив value type итератор там где напрямую Single<> используется.
Но при доступе через IEnumerable<> (как происходит в тестах) используется другой reference type итератор и каплю оптимизднул сам итератор. Но не думаю что оптимизация сильно повлияла.

Тип возвращаемого значения в методах тоже изменил на актуальный — массив в третьем тесте и Single<> в четвёртом. Но это так же на результаты тестов не должно повлиять.

На счёт логирования памяти — не совсем понял что там и как меряется, результаты обсуждать не возьмусь.

Количество итераций увеличил в десять раз до 100M.
Ниже полностью код. Сорри, компилять его надо уже Розлином.

Вот результат с последней итерации:
Итерация 3
Тест 0 запущен
Time: 75, Memory GC: 0, Memory WS: 0
Тест 1 запущен
Time: 3939, Memory GC: 2366912, Memory WS: 0
Тест 2 запущен
Time: 4670, Memory GC: 2809884, Memory WS: 0
Тест 3 запущен
Time: 4740, Memory GC: 1284604, Memory WS: 4096
Тест 4 запущен
Time: 3848, Memory GC: 2399908, Memory WS: 16384


  Код
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

internal static class Program
{
  private const int ITERATION_COUNT = 100000000; // 100M

  static void Main(string[] args) {
    var tests = new Func<int>[] { Test0, Test1, Test2, Test3, Test4, };
    for(var index = 0; index < 4; index++) {
      Console.WriteLine("Итерация {0}", index);

      for(var run = 0; run < tests.Length; run++) {
        RunTest(run, tests[run]);

        GC.Collect(0, GCCollectionMode.Forced);
        Thread.Sleep(1000);
      }
    }
  }

  private static void RunTest(int num, Func<int> test) {
    Console.WriteLine("Тест {0} запущен", num);

    var totalMemoryBefore = GC.GetTotalMemory(false);
    var workingSetBefore = Environment.WorkingSet;

    var stopwatch = Stopwatch.StartNew();
    test();
    stopwatch.Stop();

    var totalMemoryAfter = GC.GetTotalMemory(false);
    var workingSetAfter = Environment.WorkingSet;

    Console.WriteLine("Time: {0}, Memory GC: {1}, Memory WS: {2}", stopwatch.ElapsedMilliseconds,
      totalMemoryAfter - totalMemoryBefore, workingSetAfter - workingSetBefore);
  }

  private static int Test0() {
    var value = new Random(DateTime.Now.Millisecond).Next();
    for(var i = 0; i < ITERATION_COUNT; i++)
      value += value;
    return value;
  }

  private static int Test1() {
    var value = new Random(DateTime.Now.Millisecond).Next();
    for(var i = 0; i < ITERATION_COUNT; i++)
      value += value.AsEnumerable1().Sum();
    return value;
  }

  private static int Test2() {
    var value = new Random(DateTime.Now.Millisecond).Next();
    for(var i = 0; i < ITERATION_COUNT; i++)
      value += value.AsEnumerable2().Sum();
    return value;
  }

  private static int Test3() {
    var value = new Random(DateTime.Now.Millisecond).Next();
    for(var i = 0; i < ITERATION_COUNT; i++)
      value += value.AsEnumerable3().Sum();
    return value;
  }

  private static int Test4() {
    var value = new Random(DateTime.Now.Millisecond).Next();
    for(var i = 0; i < ITERATION_COUNT; i++)
      value += value.AsEnumerable4().Sum();
    return value;
  }
}
internal static class Helper
{
  public static IEnumerable<T> AsEnumerable1<T>(this T value) {
    yield return value;
  }

  public static IEnumerable<T> AsEnumerable2<T>(this T value) {
    return Enumerable.Repeat(value, 1);
  }

  public static T[] AsEnumerable3<T>(this T value) {
    return new[] { value, };
  }

  public static Single<T> AsEnumerable4<T>(this T value) {
    return new Single<T>(value);
  }
}

internal sealed class Single<T> : IEnumerable<T>
{
  public Single(T value) {
    Value = value;
  }

  private T Value { get; }

  public Enumerator GetEnumerator() => new Enumerator(this);

  IEnumerator<T> IEnumerable<T>.GetEnumerator() => new ClassEnumerator(this);
  IEnumerator IEnumerable.GetEnumerator() => new ClassEnumerator(this);

  public struct Enumerator
  {
    public Enumerator(Single<T> owner) : this() {
      Owner = owner;
    }

    private Single<T> Owner { get; set; }
    public T Current { get; private set; }

    public bool MoveNext() {
      if(Owner == null) {
        return false;
      } else {
        Current = Owner.Value;
        Owner = null;
        return true;
      }
    }
  }

  private sealed class ClassEnumerator : IEnumerator<T>
  {
    public ClassEnumerator(Single<T> owner) {
      if(owner == null) {
        throw new ArgumentNullException(nameof(owner));
      }

      Owner = owner;
    }

    private Single<T> Owner { get; set; }
    public T Current { get; private set; }

    // Does not correct (InvalidOperationException does not thrown), but usable.
    object IEnumerator.Current => Current;

    public bool MoveNext() {
      if(Owner == null) {
        return false;
      } else {
        Current = Owner.Value;
        Owner = null;
        return true;
      }
    }

    public void Reset() {
      throw new NotSupportedException();
    }

    public void Dispose() { }
  }
}
Re[4]: быстрый ToEnumerable
От: tyomchick Россия  
Дата: 28.04.15 14:35
Оценка:
Здравствуйте, xy012111, Вы писали:


X>Количество итераций увеличил в десять раз до 100M.

X>Ниже полностью код. Сорри, компилять его надо уже Розлином.

По поводу теста этот пост смотрел: http://rsdn.ru/forum/dotnet/6028544.1
Автор: tyomchick
Дата: 26.04.15
?
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.