как ускорить запрос linq в импортированном списке CSV - PullRequest
0 голосов
/ 30 ноября 2018

Мне было поручено сопоставить 1,7 миллиона записей с некоторыми результатами, которые были переданы мне в CSV-файле.

немного фона для кода ниже, у меня есть два списка ...

Сертификаты, которые содержат 5 свойств с идентификатором, эквивалентным PK.

Заказы, которые содержат список идентификаторов, которые должны содержаться в списке сертификатов.

Мне нужно сопоставитьдва и сделайте что-нибудь с теми объектами Cert, которые найдены.

            foreach (Classes.CertOrder.IDS OrderUnitID in Order.AllIDs)
            {
                var Cert = (from C in Certs where C.ID.ToUpper() == OrderUnitID.ID.ToUpper() select C).FirstOrDefault();
                if (Cert != null)
                {
                    Output.add(Cert)
                    OrderUnitID.fulfilled = true;
                }

            }

Этот код работает, но он очень медленный (чего и следовало ожидать с количеством записей) Есть ли способ, которым я могу ускорить это?

Правка Добавить, хотелось бы иметь возможность добавлять данные на сервер SQL для выполнения запросов, однако данные таковы, что не разрешается покидать рабочую станцию, на которой обрабатывается файл, илидаже разрешено трогать диск в незашифрованном виде.

В сочетании с полезным ответом ниже, я изменил свой вывод на список, предварительно отсортировав оба спискаs по ID, и теперь обработка занимает секунды, а не часы!Спасибо, переполнение стека!

Ответы [ 3 ]

0 голосов
/ 30 ноября 2018

Создать словарь из Certs:

var certsMapping = Certs
    .ToDictionary(_ => _.ID.ToUpper());

foreach (Classes.CertOrder.IDS OrderUnitID in Order.AllIDs)
{
    if (certMapping.TryGetValue(OrderUnitID.ID.ToUpper(), out var cert))
    {
        Output.add(cert);
        OrderUnitID.fulfilled = true;
    }
}
0 голосов
/ 30 ноября 2018

Расширение на принятый ответ немного других вариантов.OrdinalIgnoreCase обеспечивает лучшую однопоточную производительность, а распараллеливание - лучшую общую производительность.

class Item { public string Id { get; set; } }

class Program
{
    private static Random rng = new Random();
    private static string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    static void Main(string[] args)
    {
        var list = Enumerable.Range(1, 2_700_000)
            .Select(x => string.Join("", Enumerable.Range(5, rng.Next(20)).Select(y => characters[rng.Next(0, characters.Length)])))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .Select(x => new {Order = rng.Next(), Item = new Item {Id = x }})
            .OrderBy(x => x.Order)
            .Select(x => x.Item)
            .ToList();

        Console.WriteLine("Master List Size: {0}", list.Count);

        var matches = list.Take(350_000).Select(x => x.Id).ToList();

        Console.WriteLine("Matches List Size: {0}", matches.Count);

        var dict = list.ToDictionary(x => x.Id, x => x, StringComparer.CurrentCultureIgnoreCase);

        var results = new List<Item>();

        var sw = new Stopwatch();

        Console.WriteLine("CurrentCultureIgnoreCase Elapsed Time (avg): {0}",
            Enumerable.Range(1, 10).Select(x =>
            {
                sw.Start();

                foreach (var m in matches)
                    if (dict.TryGetValue(m, out var item))
                        results.Add(item);

                sw.Stop();

                var t = sw.ElapsedMilliseconds;

                sw.Reset();

                return t;
            }).Average());


        dict = list.ToDictionary(x => x.Id.ToUpper(), x => x);

        Console.WriteLine("ToUpper() Elapsed Time (avg): {0}",
            Enumerable.Range(1, 10).Select(x =>
            {
                sw.Start();

                foreach (var m in matches)
                    if (dict.TryGetValue(m.ToUpper(), out var item))
                        results.Add(item);

                sw.Stop();

                var t = sw.ElapsedMilliseconds;

                sw.Reset();

                return t;
            }).Average());


        dict = list.ToDictionary(x => x.Id, x => x, StringComparer.OrdinalIgnoreCase);

        Console.WriteLine("OrdinalIgnoreCase Elapsed Time (avg): {0}",
            Enumerable.Range(1, 10).Select(x =>
            {
                sw.Start();

                foreach (var m in matches)
                    if (dict.TryGetValue(m, out var item))
                        results.Add(item);

                sw.Stop();

                var t = sw.ElapsedMilliseconds;

                sw.Reset();

                return t;
            }).Average());
    }
}

var cDict = new ConcurrentDictionary<string,Item>(dict);
var cResults = new ConcurrentBag<Item>();

Console.WriteLine("Parallel Elapsed Time (avg): {0}",
    Enumerable.Range(1, 10).Select(x =>
    {
        sw.Start();

        Parallel.ForEach(matches, new ParallelOptions{MaxDegreeOfParallelism = 20}, m =>
        {
            if (cDict.TryGetValue(m, out var item))
                cResults.Add(item);
        });
        sw.Stop();

        var t = sw.ElapsedMilliseconds;

        sw.Reset();

        return t;
    }).Average());

Результаты

 Master List Size: 2158882
 Matches List Size: 350000
 CurrentCultureIgnoreCase Elapsed Time (avg): 298.2
 ToUpper() Elapsed Time (avg): 179.6
 OrdinalIgnoreCase Elapsed Time (avg): 163.9
 Parallel Elapsed Time (avg): 74.6
0 голосов
/ 30 ноября 2018

Почему поиск в базе данных быстрее?одной из причин являются индексы

, которые можно использовать i4o для создания индекса для списков в памяти.

А затем использовать параллельный цикл for-each для ускорения работыдо.

...