DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Berill Азербайджан  
Дата: 02.05.16 22:32
Оценка:
Доброго всем.
В данный момент переписываю метод , в котором используются DataSet'ы и DataTable'ы.
Суть простая. Из БД получаем некий начальный курсор данных (DataSet). Далее в методе происходит добавление строк к DataSet.Tables[0] при выполнении определенных условий. При этом для исключения дублирования строк применяется DataTable.Select(). Код выглядит примерно так:
string filter = string.Format("TYPE_CODE={0} AND DETAIL_ID={1}", addingRowInfo.TypeCode, addingRowInfo.DetailID);
if (dataSet.Tables[0].Select(filter).Length == 0)
{
    // добавление новой строки в dataSet.Tables[0]
}
И так пару десятков раз для каждого типа добавляемой строки (фильтр может быть различным).

Я пришел к выводу, что мне, по сути, нужен всего лишь List<T> вместо DataTable'а (я уж молчу про DataSet). Я задался вопросом: насколько быстро будет работа с List<T>.Any по сравнению с DataTable.Select.

  Код
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;

namespace DataTableSelect
{
    static class DataTableExtensions
    {
        public static DataTable ToDataTable<T>(this IList<T> data)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
            DataTable table = new DataTable();
            foreach ( PropertyDescriptor prop in properties )
            {
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }

            foreach ( T item in data )
            {
                DataRow row = table.NewRow();
                foreach ( PropertyDescriptor prop in properties )
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                }
                table.Rows.Add(row);
            }
            return table;
        }
    }

    public class MyData
    {
        public int Code { get; set; }
        public string Period { get; set; }
        public int TypeCode { get; set; }
        public int DetailID { get; set; }

        public override string ToString()
        {
            return string.Format("Code: {0}; Period: {1}; TypeCode: {2}; DetailID: {3};", Code, Period, TypeCode, DetailID);
        }
    }

    class Program
    {
        static void Main()
        {
            Console.WindowWidth = 120;

            int count = 1 * 1000;
            List<MyData> myList = CreateTestList();
            DataTable table = myList.ToDataTable();
            GetResultsForList(count, myList);
            GetResultsForTableWithLINQ(count, table);
            GetResultsForTableWithSelect(count, table);

            count = 2 * 1000;
            GetResultsForList(count, myList);
            GetResultsForTableWithLINQ(count, table);
            GetResultsForTableWithSelect(count, table);

            count = 4 * 1000;
            GetResultsForList(count, myList);
            GetResultsForTableWithLINQ(count, table);
            GetResultsForTableWithSelect(count, table);

            count = 8 * 1000;
            GetResultsForList(count, myList);
            GetResultsForTableWithLINQ(count, table);
            GetResultsForTableWithSelect(count, table);

            count = 16 * 1000;
            GetResultsForList(count, myList);
            GetResultsForTableWithLINQ(count, table);
            GetResultsForTableWithSelect(count, table);

            count = 100 * 1000;
            GetResultsForList(count, myList);
            GetResultsForTableWithLINQ(count, table);
            GetResultsForTableWithSelect(count, table);

            Console.WriteLine("Done...");
            Console.ReadLine();
        }

        private static void GetResultsForList(int count, List<MyData> myList)
        {
            Measure("List",
                    () =>
                    {
                        for ( int i = 0; i < count; i++ )
                        {
                            foreach ( var myData in myList )
                            {
                                if ( myData.Period == "01.01.2016" && myData.DetailID == 10 )
                                {
                                    break;
                                }
                            }
                        }
                        return count;
                    });
        }

        private static void GetResultsForTableWithLINQ(int count, DataTable table)
        {
            Measure("Table LINQ",
                    () =>
                    {
                        for ( int i = 0; i < count; i++ )
                        {
                            var q = table.AsEnumerable().Where(row => row.Field<string>("Period") == "01.01.2016" && row.Field<int>("DetailID") == 10);
                            var found = q.ToList().Count != 0;
                        }
                        return count;
                    });
        }

        private static void GetResultsForTableWithSelect(int count, DataTable table)
        {
            string filter2 = "Period='01.01.2016' AND DetailID=10";

            Measure("Table Select",
                    () =>
                    {
                        for ( int i = 0; i < count; i++ )
                        {
                            var found = table.Select(filter2).Length != 0;

                        }
                        return count;
                    });
            Console.WriteLine("=========================================================================");
        }

        private static List<MyData> CreateTestList()
        {
            List<MyData> foos = new List<MyData>();
            for ( int i = 0; i < 20; i++ )
            {
                foos.Add(new MyData
                {
                    Code = i,
                    TypeCode = i,
                    Period = i == 10 ? "01.01.2016" : string.Empty,
                    DetailID = i == 10 ? 10 : 0
                });
            }

            return foos;
        }

        static void Measure(string name, Func<long> callback)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            var mem = GC.GetTotalMemory(true);
            var gc00 = GC.CollectionCount(0);
            var gc01 = GC.CollectionCount(1);
            var gc02 = GC.CollectionCount(2);

            var sw = Stopwatch.StartNew();
            var result = callback();
            sw.Stop();

            var mem2 = GC.GetTotalMemory(false);
            var gc10 = GC.CollectionCount(0);
            var gc11 = GC.CollectionCount(1);
            var gc12 = GC.CollectionCount(2);

            var memDelta = (mem2 - mem) / 1024.0;
            var gcDelta0 = gc10 - gc00;
            var gcDelta1 = gc11 - gc01;
            var gcDelta2 = gc12 - gc02;

            Console.WriteLine(
                "{0,25}: {1,5}ms, ips: {2,22:N} | Mem: {3,6:N2} kb, GC 0/1/2: {4}/{5}/{6} => {7,6}",
                name, sw.ElapsedMilliseconds, result / sw.Elapsed.TotalSeconds, memDelta, gcDelta0, gcDelta1, gcDelta2, result);
        }
    }
}


  Результаты
                     List:    26ms, ips:              37 057.49 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   1000
               Table LINQ:    69ms, ips:              14 329.03 | Mem: 454.95 kb, GC 0/1/2: 0/0/0 =>   1000
             Table Select:    47ms, ips:              21 209.94 | Mem: 1 681.93 kb, GC 0/1/2: 0/0/0 =>   1000
=========================================================================
                     List:    51ms, ips:              38 831.33 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   2000
               Table LINQ:    89ms, ips:              22 231.16 | Mem: 912.00 kb, GC 0/1/2: 0/0/0 =>   2000
             Table Select:    17ms, ips:             113 244.51 | Mem: 280.59 kb, GC 0/1/2: 1/0/0 =>   2000
=========================================================================
                     List:   101ms, ips:              39 299.02 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   4000
               Table LINQ:   180ms, ips:              22 202.92 | Mem: 1 816.00 kb, GC 0/1/2: 0/0/0 =>   4000
             Table Select:    34ms, ips:             116 006.93 | Mem: 562.63 kb, GC 0/1/2: 2/0/0 =>   4000
=========================================================================
                     List:   202ms, ips:              39 581.31 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   8000
               Table LINQ:   355ms, ips:              22 495.04 | Mem: 560.66 kb, GC 0/1/2: 1/0/0 =>   8000
             Table Select:    68ms, ips:             117 283.31 | Mem: 1 123.83 kb, GC 0/1/2: 4/0/0 =>   8000
=========================================================================
                     List:   403ms, ips:              39 649.04 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>  16000
               Table LINQ:   712ms, ips:              22 464.26 | Mem: 1 113.33 kb, GC 0/1/2: 2/0/0 =>  16000
             Table Select:   138ms, ips:             115 749.28 | Mem: 2 249.10 kb, GC 0/1/2: 8/0/0 =>  16000
=========================================================================
                     List:  2530ms, ips:              39 510.26 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 => 100000
               Table LINQ:  4459ms, ips:              22 424.43 | Mem: 2 320.98 kb, GC 0/1/2: 14/0/0 => 100000
             Table Select:   864ms, ips:             115 615.97 | Mem: 1 738.18 kb, GC 0/1/2: 54/0/0 => 100000
=========================================================================
Done...


Не ожидал таких результатов. Думал, что работать со списком будет быстрее. Может я что-то не так делаю?
Re: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 02.05.16 22:43
Оценка: 2 (1) +3
Здравствуйте, Berill, Вы писали:


B>Не ожидал таких результатов. Думал, что работать со списком будет быстрее. Может я что-то не так делаю?


List не сортированный, он каждый раз обходится заново. DataTable же, скорее всего, использует индексы в памяти — это хорошо видно по потребляемой памяти, там разница на несколько порядков между List, LINQ и DataTable.
Чтобы получить хорошую производительность без DataTable, нужно брать индексированные структуры данных. Dictionary, например.
С уважением, Artem Korneev.
Re[2]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Berill Азербайджан  
Дата: 03.05.16 00:04
Оценка:
Здравствуйте, Artem Korneev, Вы писали:

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



B>>Не ожидал таких результатов. Думал, что работать со списком будет быстрее. Может я что-то не так делаю?


AK>List не сортированный, он каждый раз обходится заново. DataTable же, скорее всего, использует индексы в памяти — это хорошо видно по потребляемой памяти, там разница на несколько порядков между List, LINQ и DataTable.

AK>Чтобы получить хорошую производительность без DataTable, нужно брать индексированные структуры данных. Dictionary, например.

Спасибо.
Насчет сортировки надо будет подумать, посмотреть какие типы фильтров чаще всего применяются. У меня там 15 полей в таблице, в основном фильтрация идет по 3, но в разной комбинации:
1. По одному из полей ;
2. По паре полей;
3. По всей тройке.

В связи с этим, не вижу как сюда приспособить вариант с Dictionary.
Re[3]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 03.05.16 00:17
Оценка: 2 (1) +1
Здравствуйте, Berill, Вы писали:

B>Насчет сортировки надо будет подумать, посмотреть какие типы фильтров чаще всего применяются. У меня там 15 полей в таблице, в основном фильтрация идет по 3, но в разной комбинации:

B>1. По одному из полей ;
B>2. По паре полей;
B>3. По всей тройке.

B>В связи с этим, не вижу как сюда приспособить вариант с Dictionary.


В этом сценарии вам нужна коллекция для хранения данных и индексы к ней. Индексы нужно обновлять после операций удаления/вставки. Если удаления данных не происходит, то в качестве коллекции можно взять обычный List, а индексы реализовать через либо через Dictionary, либо через SortedDictionary (если нужно не только строгое соответствие, но и диапазоны, сортировка по полю и т.д.). Если значение ключа в этом индексе не уникально, тогда значением в Dictionary должна быть не ссылка на элемент, а List со ссылками на элементы с одинаковым значением индекса. Индексировать нужно каждое поле, по которому нужна быстрая выборка.

А вот насколько оно в этом варианте будет быстрее готовой имплементации в виде DataTable и стоит ли овчинка выделки — это уже нужно по обстоятельствам смотреть.
С уважением, Artem Korneev.
Re[4]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Berill Азербайджан  
Дата: 03.05.16 09:22
Оценка:
Здравствуйте, Artem Korneev, Вы писали:

AK>В этом сценарии вам нужна коллекция для хранения данных и индексы к ней. Индексы нужно обновлять после операций удаления/вставки. Если удаления данных не происходит, то в качестве коллекции можно взять обычный List, а индексы реализовать через либо через Dictionary, либо через SortedDictionary (если нужно не только строгое соответствие, но и диапазоны, сортировка по полю и т.д.). Если значение ключа в этом индексе не уникально, тогда значением в Dictionary должна быть не ссылка на элемент, а List со ссылками на элементы с одинаковым значением индекса. Индексировать нужно каждое поле, по которому нужна быстрая выборка.


AK>А вот насколько оно в этом варианте будет быстрее готовой имплементации в виде DataTable и стоит ли овчинка выделки — это уже нужно по обстоятельствам смотреть.


Спасибо за советы.
Re: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Mihas  
Дата: 03.05.16 11:02
Оценка: 2 (1)
Здравствуйте, Berill, Вы писали:

Советы еще принимаются?

Вместо тормознутого DataTable.Select() мне нравится такой способ выборки нужных строк:

var allRows = table.Rows.Cast<DataRow>();
var filteredRows = allRows.Where(r => r["TYPE_CODE"] == addingRowInfo.TypeCode).Where(r => r["DETAIL_ID"] == addingRowInfo.DetailID).ToList();


Если избавиться от замыканий, то работает вполне себе шустро.

Пополнять же таблицу новыми строками можно методом Merge(). Он умеет отслеживать дубликаты по первичному ключу.

PS. А еще есть чудесный метод DataTable.Rows.Find(). Он работает только с первичным ключом, но очень быстро. В нашем случае было настолько быстро, что было выгоднее переназначить первичный ключ на другую колонку, чем использовать DataTable.Select().

Извините, если посоветовал банальность.
Отредактировано 03.05.2016 11:17 Mihas . Предыдущая версия . Еще …
Отредактировано 03.05.2016 11:16 Mihas . Предыдущая версия .
Re[4]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Lexey Россия  
Дата: 03.05.16 11:24
Оценка: +2
Здравствуйте, Artem Korneev, Вы писали:

AK>В этом сценарии вам нужна коллекция для хранения данных и индексы к ней. Индексы нужно обновлять после операций удаления/вставки. Если удаления данных не происходит, то в качестве коллекции можно взять обычный List, а индексы реализовать через либо через Dictionary, либо через SortedDictionary (если нужно не только строгое соответствие, но и диапазоны, сортировка по полю и т.д.). Если значение ключа в этом индексе не уникально, тогда значением в Dictionary должна быть не ссылка на элемент, а List со ссылками на элементы с одинаковым значением индекса. Индексировать нужно каждое поле, по которому нужна быстрая выборка.


Можно еще и сам List отсортировать по наиболее часто используемой комбинации полей, а потом использовать бинарный поиск (в том числе и по частичному ключу).
"Будь достоин победы" (c) 8th Wizard's rule.
Re[3]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 03.05.16 12:28
Оценка: +1
Здравствуйте, Berill, Вы писали:

B>В связи с этим, не вижу как сюда приспособить вариант с Dictionary.


Dictionary не очень годится для индексов, так как помогает только для поиска точного соответствия и джойна. Хотя в твоем варианте хватит и его — просто сделай композитный ключ по TYPE_CODE и DETAIL_ID. Т.е. словарик типа Dictionary<ValueTuple<int, int>, int>. Где ключ ValueTuple<int, int> содержит комбинацию TypeCode и DetailId, а значение — индекс записи в списке (ValueTuple можно взять в CodeJam или написать самому).
В более общем случае лучше для индексов использовать не хеш-таблицу, а бинарное дерево. В фреймворке есть одна реализация — SortedDictionary (внутри там, емнип, R-B Tree).

P.S. Если в реальном коде записей в списке много, и операций поиска по нему тоже много, то имеет смысл распараллелить код через банальный Parallel.ForEach, заменив Dictionary на ConcurrentDictionary.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Berill Азербайджан  
Дата: 04.05.16 08:30
Оценка:
Здравствуйте, Mihas, Вы писали:

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


M>Советы еще принимаются?


M>Вместо тормознутого DataTable.Select() мне нравится такой способ выборки нужных строк:


M>
M>var allRows = table.Rows.Cast<DataRow>();
M>var filteredRows = allRows.Where(r => r["TYPE_CODE"] == addingRowInfo.TypeCode).Where(r => r["DETAIL_ID"] == addingRowInfo.DetailID).ToList();
M>


M>Если избавиться от замыканий, то работает вполне себе шустро.


M>Пополнять же таблицу новыми строками можно методом Merge(). Он умеет отслеживать дубликаты по первичному ключу.


M>PS. А еще есть чудесный метод DataTable.Rows.Find(). Он работает только с первичным ключом, но очень быстро. В нашем случае было настолько быстро, что было выгоднее переназначить первичный ключ на другую колонку, чем использовать DataTable.Select().


M>Извините, если посоветовал банальность.


У меня нет никаких ключей и индексы для таблицы я сам не создаю (пока что).
В основном идет поиск по трем полям (мне не нужны сами эти записи, мне нужен факт их наличия или отсутствия), а также еще один часто используемый цикл по этой таблице — нахождение максимального значения поля Code для TYPE_CODE == addingRowInfo.TypeCode.

Мои замеры показали, что код
        private static void GetFindResultsForTableWithCast(int count, DataTable table)
        {
            Measure("Table Cast",
                    () =>
                    {
                        for ( int i = 0; i < count; i++ )
                        {
                            var allRows = table.Rows.Cast<DataRow>();
                            var found = allRows.Where(r => (int)r["TypeCode"] == 10 
                                                        && (string)r["Period"] == "01.01.2016" 
                                                        && (int)r["DetailID"] == 5)
                                               .ToList()
                                               .Count != 0;
                        }
                        return count;
                    });
        }

ничуть не шустрее Select'a =(

А вот если сделать так:
  код
        private static void GetFindResultsForList(int count, List<MyData> myList)
        {
            //myList.Sort(new MyDataComparer());
            Measure("List",
                    () =>
                    {
                        //myList.Sort(new MyDataComparer());
                        for ( int i = 0; i < count; i++ )
                        {
                            bool found = false;
                            foreach ( var myData in myList )
                            {
                                if ( myData.TypeCode == 10 && myData.Period == "01.01.2016" && myData.DetailID == 5 )
                                {
                                    found = true;
                                    break;
                                }
                            }
                        }
                        return count;
                    });
        }

        private static void GetFindResultsForTableWithLINQ(int count, DataTable table)
        {
            Measure("Table LINQ",
                    () =>
                    {
                        for ( int i = 0; i < count; i++ )
                        {
                            var found = table.AsEnumerable().Any(row => row.Field<int>("TypeCode") == 10
                                                                     && row.Field<string>("Period") == "01.01.2016"
                                                                     && row.Field<int>("DetailID") == 5
                                                                );
                            /*
                            var q = from r in table.AsEnumerable()
                                    where r.Field<string>("Period") == "01.01.2016" 
                                       && r.Field<int>("DetailID") == 5 
                                       && r.Field<int>("TypeCode") == 10
                                    select r;
                            var found = q.ToList().Count != 0;
                             */
                        }
                        return count;
                    });
        }

        private static void GetFindResultsForTableWithSelect(int count, DataTable table)
        {
            string filter2 = "TypeCode=10 AND Period='01.01.2016' AND DetailID=5";

            Measure("Table Select",
                    () =>
                    {
                        for ( int i = 0; i < count; i++ )
                        {
                            var found = table.Select(filter2).Length != 0;
                        }
                        return count;
                    });
        }

        private static void GetFindResultsForTableWithCast(int count, DataTable table)
        {
            Measure("Table Cast",
                    () =>
                    {
                        for ( int i = 0; i < count; i++ )
                        {
                            var allRows = table.Rows.Cast<DataRow>();
                            var found = allRows.Any(r => (int)r["TypeCode"] == 10 
                                                      && (string)r["Period"] == "01.01.2016" 
                                                      && (int)r["DetailID"] == 5);
                        }
                        return count;
                    });
        }

то получаем
  результаты
                     List:    39ms, ips:              25 265.16 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   1000
               Table LINQ:    48ms, ips:              20 634.98 | Mem: 326.80 kb, GC 0/1/2: 0/0/0 =>   1000
             Table Select:    65ms, ips:              15 309.53 | Mem: 1 993.93 kb, GC 0/1/2: 0/0/0 =>   1000
               Table Cast:    31ms, ips:              32 136.05 | Mem: 200.00 kb, GC 0/1/2: 0/0/0 =>   1000
=========================================================================
                     List:    64ms, ips:              30 839.35 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   2000
               Table LINQ:    59ms, ips:              33 874.99 | Mem: 656.00 kb, GC 0/1/2: 0/0/0 =>   2000
             Table Select:    28ms, ips:              69 021.21 | Mem: 912.12 kb, GC 0/1/2: 1/0/0 =>   2000
               Table Cast:    58ms, ips:              33 928.78 | Mem: 392.00 kb, GC 0/1/2: 0/0/0 =>   2000
=========================================================================
                     List:   129ms, ips:              30 984.09 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   4000
               Table LINQ:   118ms, ips:              33 765.28 | Mem: 1 304.00 kb, GC 0/1/2: 0/0/0 =>   4000
             Table Select:    56ms, ips:              70 307.91 | Mem: 1 817.10 kb, GC 0/1/2: 2/0/0 =>   4000
               Table Cast:   116ms, ips:              34 275.01 | Mem: 784.00 kb, GC 0/1/2: 0/0/0 =>   4000
=========================================================================
                     List:   256ms, ips:              31 249.65 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>   8000
               Table LINQ:   238ms, ips:              33 592.53 | Mem: 2 600.00 kb, GC 0/1/2: 0/0/0 =>   8000
             Table Select:   113ms, ips:              70 718.98 | Mem: 558.41 kb, GC 0/1/2: 5/0/0 =>   8000
               Table Cast:   237ms, ips:              33 739.74 | Mem: 1 568.00 kb, GC 0/1/2: 0/0/0 =>   8000
=========================================================================
                     List:   512ms, ips:              31 216.53 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 =>  16000
               Table LINQ:   484ms, ips:              33 021.56 | Mem: 2 120.03 kb, GC 0/1/2: 1/0/0 =>  16000
             Table Select:   225ms, ips:              70 867.42 | Mem: 1 108.82 kb, GC 0/1/2: 10/0/0 =>  16000
               Table Cast:   469ms, ips:              34 073.37 | Mem:  56.09 kb, GC 0/1/2: 1/0/0 =>  16000
=========================================================================
                     List:  3210ms, ips:              31 151.97 | Mem:   8.00 kb, GC 0/1/2: 0/0/0 => 100000
               Table LINQ:  2980ms, ips:              33 551.23 | Mem: 1 705.42 kb, GC 0/1/2: 10/0/0 => 100000
             Table Select:  1421ms, ips:              70 349.51 | Mem: 2 297.59 kb, GC 0/1/2: 64/0/0 => 100000
               Table Cast:  2953ms, ips:              33 854.11 | Mem: 1 104.40 kb, GC 0/1/2: 6/0/0 => 100000
=========================================================================
Done...
Re: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 04.05.16 08:55
Оценка: 6 (2)
Здравствуйте, Berill, Вы писали:

Запустил твой тест на своей машине под свежим х64 фреймворком
                     List:     0ms, ips:           2 003 205,13 | Mem:   8,00 kb, GC 0/1/2: 0/0/0 =>   1000
               Table LINQ:     8ms, ips:             123 313,69 | Mem: 442,22 kb, GC 0/1/2: 0/0/0 =>   1000
             Table Select:     9ms, ips:             106 875,29 | Mem: 1 649,29 kb, GC 0/1/2: 0/0/0 =>   1000
=========================================================================
                     List:     0ms, ips:           5 640 157,92 | Mem:   8,00 kb, GC 0/1/2: 0/0/0 =>   2000
               Table LINQ:     7ms, ips:             265 748,95 | Mem: 880,00 kb, GC 0/1/2: 0/0/0 =>   2000
             Table Select:    15ms, ips:             126 868,94 | Mem: 3 293,53 kb, GC 0/1/2: 0/0/0 =>   2000
=========================================================================
                     List:     0ms, ips:           5 239 717,06 | Mem:   8,00 kb, GC 0/1/2: 0/0/0 =>   4000
               Table LINQ:    14ms, ips:             273 334,20 | Mem: 1 752,00 kb, GC 0/1/2: 0/0/0 =>   4000
             Table Select:    29ms, ips:             136 691,85 | Mem: 2 489,11 kb, GC 0/1/2: 1/0/0 =>   4000
=========================================================================
                     List:     1ms, ips:           5 599 496,05 | Mem:   8,00 kb, GC 0/1/2: 0/0/0 =>   8000
               Table LINQ:    30ms, ips:             266 198,16 | Mem: 3 504,00 kb, GC 0/1/2: 0/0/0 =>   8000
             Table Select:    69ms, ips:             115 682,83 | Mem: 875,38 kb, GC 0/1/2: 3/0/0 =>   8000
=========================================================================
                     List:     2ms, ips:           5 428 697,45 | Mem:   8,00 kb, GC 0/1/2: 0/0/0 =>  16000
               Table LINQ:    68ms, ips:             233 165,11 | Mem: 2 912,54 kb, GC 0/1/2: 1/0/0 =>  16000
             Table Select:   125ms, ips:             127 779,20 | Mem: 1 743,00 kb, GC 0/1/2: 6/0/0 =>  16000
=========================================================================
                     List:    19ms, ips:           5 239 195,47 | Mem:   8,00 kb, GC 0/1/2: 0/0/0 => 100000
               Table LINQ:   378ms, ips:             264 321,26 | Mem: 2 797,77 kb, GC 0/1/2: 10/0/0 => 100000
             Table Select:   827ms, ips:             120 914,44 | Mem: 661,75 kb, GC 0/1/2: 40/0/0 => 100000
=========================================================================
Done...

... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Berill Азербайджан  
Дата: 04.05.16 10:51
Оценка:
Здравствуйте, AndrewVK, Вы писали:

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


AVK>Запустил твой тест на своей машине под свежим х64 фреймворком


AVK>


мдя... я-то по привычке всегда запускаю просто через F5.... пепел мне на голову((( пошел биться головой об стенку...
Извиняюсь перед всеми.
Re[3]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Sinix  
Дата: 04.05.16 11:06
Оценка:
Здравствуйте, Berill, Вы писали:

B>мдя... я-то по привычке всегда запускаю просто через F5....

Для этого Ctrl-F5 есть

+ релизную сборку естественно.
Re[3]: DataTable.Select vs List<T>.Any vs LINQ To DataTable
От: Lexey Россия  
Дата: 04.05.16 15:42
Оценка: +1
Здравствуйте, Berill, Вы писали:

B>У меня нет никаких ключей и индексы для таблицы я сам не создаю (пока что).

B>В основном идет поиск по трем полям (мне не нужны сами эти записи, мне нужен факт их наличия или отсутствия)

HashSet<ValueTuple<fieldT1,fieldT2,fieldT3>> в помощь. Один раз заполняешь данными из таблицы, потом делаешь проверки за околоконстантное время.

B>, а B>также еще один часто используемый цикл по этой таблице — нахождение максимального значения поля Code для TYPE_CODE B>== addingRowInfo.TypeCode.


Если это разовое действие (а оно разовое, если набор строк не меняется), то проще все одним сканом по таблице типа:
table.Rows.Cast<DataRow>().Where(_ => (int)_["TypeCode"] == addingRowInfo.TypeCode).Select(_ => (int)_["Code]).Max();

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


B>Мои замеры показали, что код

B>
        private static void GetFindResultsForTableWithCast(int count, DataTable table)
B>        {
B>            Measure("Table Cast",
B>                    () =>
B>                    {
B>                        for ( int i = 0; i < count; i++ )
B>                        {
B>                            var allRows = table.Rows.Cast<DataRow>();
B>                            var found = allRows.Where(r => (int)r["TypeCode"] == 10 
B>                                                        && (string)r["Period"] == "01.01.2016" 
B>                                                        && (int)r["DetailID"] == 5)
B>                                               .ToList()
B>                                               .Count != 0;
B>                        }
B>                        return count;
B>                    });
B>        }

B>ничуть не шустрее Select'a =(

Зачем делать полную выборку и потом преобразовывать ее в List только ради того, чтобы сравнить Count с нулем?
Тем более, что дальше идет правильный вариант с Any.
"Будь достоин победы" (c) 8th Wizard's rule.
Отредактировано 04.05.2016 15:51 Lexey . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.