Почему LINQ такой тормозной?

user7320
Дата: 29.05.2014 14:03:05
Skip(n) is O(n).

Получается, что, если n достаточно велико, то быстрее (по выполнению, а не по написанию) будет страрый добый for сделать, где сразу начать операции с нужного индекса. И с фором будет O(1).

Я понимаю, что LINQ надо использовать только в местах, где скорость не важна. Но всё же не могли бы они его немного ускорить? Ну чтобы хотя бы вот такие вещи, как Skip.

Люди страшные вещи говорят: http://stackoverflow.com/questions/9718117/selecting-first-10-records-then-next-10-paging-using-linq#comment12354639_9718155 .
Lelouch
Дата: 29.05.2014 14:15:15
user7320,

Как по вашему работает Skip ? - это и даст пояснение почему он "тормозной".
P.S. Производительность LINQ To Object вообще не может сильно отличаться от производительности соответствующего набора циклов foreach...
P.P.S. И естественно for тут оказывается быстрее, потому что использует не перебор энумератора, а доступ по индексу.
Где-то в степи
Дата: 29.05.2014 14:20:42
user7320,
все в природе енумераторов..
прикурите пож. такой пример..
 class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<int> s = GetInts();
            //foreach (var i in s)
            //{
            //    Console.Write(i);
            //}
            for (int i = 0; i < s.Count(); i++)
            {
                Console.Write(i);
            }

        }

        static IEnumerable<int> GetInts()
        {
            while (true)
            {
                yield return new Random().Next(int.MinValue, int.MaxValue);
            }
        }
    }
Lelouch
Дата: 29.05.2014 14:22:03
Где-то в степи,

там код в цикле ни разу не выполнится, потому что на Count() будет вечный цикл.
user7320
Дата: 29.05.2014 14:24:50
Lelouch
user7320,

Как по вашему работает Skip ? - это и даст пояснение почему он "тормозной".
P.S. Производительность LINQ To Object вообще не может сильно отличаться от производительности соответствующего набора циклов foreach...
P.P.S. И естественно for тут оказывается быстрее, потому что использует не перебор энумератора, а доступ по индексу.

1. А что, они не могли там внутри, в реализации, условие добавить, чтобы скипать сразу на нужный индекс?

2. Я так понимаю, в Линке вообще нет быстрой (O(1)) операции для начала работы с произвольного непервого индекса?

3. А как насчёт "дойти не до конца"? Надеюсь, Take(5) по коллекции из 10 элементов не заставляет Линк делать ещё 5 скипов после первых пяти элементов?
user7320
Дата: 29.05.2014 14:26:14
Где-то в степи
все в природе енумераторов..

Да знаю я (кстати, спасибо, что напомнили - постоянно забываю).

А почему нельзя для узких мест сделать отдельные проверки и ускорить эти места? Внутри Линка, я имею ввиду.
Lelouch
Дата: 29.05.2014 14:30:38
user7320,

user7320
А что, они не могли там внутри, в реализации, условие добавить, чтобы скипать сразу на нужный индекс?


1) Ну напишите свой Ext метод, который будет переопределять поведение для List ))

user7320
Я так понимаю, в Линке вообще нет быстрой (O(1)) операции для начала работы с произвольного непервого индекса?


2) На форуме упоминалось, что, например ElementAt для List именно по индексу работает

user7320
А как насчёт "дойти не до конца"? Надеюсь, Take(5) по коллекции из 10 элементов не заставляет Линк делать ещё 5 скипов после первых пяти элементов?


3) А вы таки разберитесь как LINQ работает)) Ответ: нет, о причинах подумайте сами )
Где-то в степи
Дата: 29.05.2014 14:30:45
user7320,
давай еще пример, измени код
  static void Main(string[] args)
        {
            IEnumerable<int> s = GetInts();
            //foreach (var i in s)
            //{
            //    Console.Write(i);
            //}
            for (int i = 0; i < s.Count(); i++)
            {
                Console.Write(i);
            }

        }

        static IEnumerable<int> GetInts()
        {
            var i = 0;
            while (i<10)
            {
                i++;
                yield return new Random().Next(int.MinValue, int.MaxValue);
            }
        }

и посмотри под отладчиком, как ведет себя метод s.Count() при каждой итерации по циклу.
Где-то в степи
Дата: 29.05.2014 14:58:24
user7320,
не слышу возмущений, что абстракция потекла..
как быть?
           var enumerable = s as int[] ?? s.ToArray(); 
            for (int i = 0; i < enumerable.Count(); i++)
            {
                Console.Write(i);
            }

а если много? сожрет память(, зато быстрый доступ)
Это Вам решать, есть расширения и вперед..
И обещанная головоломка, ( не от меня) типа: а схуяли так?
 static void Main(string[] args)
        {
            var ob = new { enumerator = new List<int> { 1, 2 }.GetEnumerator() };
            while (ob.enumerator.MoveNext()) 
            {
                Console.Write(ob.enumerator.Current);
            }
        }
user7320
Дата: 29.05.2014 16:39:36
Где-то в степи
И обещанная головоломка, ( не от меня) типа: а схуяли так?
 static void Main(string[] args)
        {
            var ob = new { enumerator = new List<int> { 1, 2 }.GetEnumerator() };
            while (ob.enumerator.MoveNext()) 
            {
                Console.Write(ob.enumerator.Current);
            }
        }

Это связано с анонимными типами. Чёта как-то так, хотя и не совсем в тему, по-моему (т. к. в вашем примере никто этот анонимный тип не заставляет реализовывать интерфейс, у него есть всего лишь свойство с таким интерфейсом).

Вот так работает

        class My
        {
            public IEnumerator<int> enumerator;
        }

        static void Main(string[] args)
        {
            var ob = new My() { enumerator = new List<int> { 1, 2 }.GetEnumerator() };

            while (ob.enumerator.MoveNext())
            {
                Console.Write(ob.enumerator.Current);
            }

            Console.Read();
        }


Ну а вообще, я думаю, сборщик мусора постарался. Списка уже не существует. Вернее,