Ускорить циклический просмотр словаря в VB.Net 2008

#.net #vb.net #dictionary

#.net #vb.net #словарь

Вопрос:

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

Чтобы ускорить этот процесс и предотвратить любые проблемы с таймаутом, которые могут возникнуть у людей, пытающихся использовать сайт отчетов, использующий ту же базу данных, я изменил код, чтобы преобразовать несколько таблиц в словари, а затем выполнить итерацию по ним, чтобы увидеть, существует ли этот клиент / адрес / дилерский центр, и извлечь идентификатор из ключа словаря, если он существует, или вставить его в таблицу, а затем в словарь, если нет.

Однако в настоящее время я обнаружил, что это выполняется медленнее, чем если бы я запрашивал базу данных несколько раз для каждой регистрации. Одна из возможных причин, о которой я могу думать, заключается в том, что мои словари довольно большие (в одном 8 миллионов записей, а в другом 11 миллионов).

Вот один из примеров того, что я делаю:

     For Each kvp As KeyValuePair(Of Int64, String) In dCust
            If kvp.Value = firstName amp; "|" amp; lastName amp; "|" amp; companyName amp; "|" amp; addrId amp; "|" amp; typeID amp; "|" amp; phone amp; "|" amp; email Then
                custId = kvp.Key
                Exit For
            End If
    Next
  

В этом словаре содержится около 11 миллионов записей.

Идея, которая пришла в голову моему коллеге, заключалась в том, чтобы запустить Dictionary.ContainsValue() перед циклом, чтобы посмотреть, есть ли он вообще. И если это не так, пропустите цикл полностью. Я бы хотел попробовать это, только если это выполняется быстрее, чем просто выполнение самого цикла, если они занимают одинаковое время, я не вижу смысла в повторном запуске цикла. дважды.

Итак, мои вопросы к вам:

  • Делаю ли я это наиболее эффективным способом?
  • Было бы быстрее запустить Dictionary.ContainsValue() перед попыткой выполнения цикла, или система интерпретирует их как одно и то же, тем самым удваивая мое время?
  • Есть ли что-нибудь еще, что я должен искать?

Комментарии:

1. Вы выполняете вычисления firstName amp; "|" amp; lastName amp; "|" amp; companyName amp; "|" amp; addrId amp; "|" amp; typeID amp; "|" amp; phone amp; "|" amp; email 11 миллионов раз? Попробуйте вычислить это только один раз перед циклом.

2. @Gabe, ты опередил меня на 50 секунд!

3. Всегда ли Dictionary.Values уникален? Если это так, инвертируйте ваши пары ключ-значение (как отмечается в нескольких ответах) — хорошая идея.

Ответ №1:

Одной очевидной небольшой оптимизацией было бы выполнение конкатенации firstName , lastName и т.д. один раз вне цикла. В настоящее время вы выполняете объединение на каждой итерации цикла, что, очевидно, происходит медленнее, чем могло бы быть.

Нет, использование ContainsValue не было бы быстрее — все равно придется выполнять линейный поиск.

Очевидной большой оптимизацией было бы инвертировать словарь — создать Dictionary(Of String, Int64) который в основном имеет идентификатор для каждого строкового значения. В настоящее время вы не используете естественные преимущества словаря — вы, по сути, рассматриваете его как список пар ключ / значение.

Вы действительно используете словарь обычным способом (просматриваете по ключу)?

Ответ №2:

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

Повторяя пары ключ-значение в словаре, вы сводите на нет преимущество, которое предоставляет словарь (хэш-таблица) — быстрый поиск заданного значения ключа.

Вы должны использовать словарь (String, Int64), сопоставляя firatname, lastname, … с CustID. Поиск в этом будет очень быстрым по сравнению с тем, что вы делаете в данный момент.

Ответ №3:

Одна вещь, которую вы могли бы сделать, чтобы ускорить процесс, — это предварительно объединить эту строку поиска:

 Dim SearchValue as String = firstName amp; "|" amp; lastName amp; "|" amp; companyName amp; "|" amp; addrId amp; "|" amp; typeID amp; "|" amp; phone amp; "|" amp; email
For Each kvp As KeyValuePair(Of Int64, String) In dCust
        If kvp.Value = SearchValue Then
            custId = kvp.Key
            Exit For
        End If
Next
  

Ответ №4:

Смысл использования словаря в том, чтобы выполнить быстрый поиск по КЛЮЧУ, а не по значению. Либо просто используйте обычный список массивов, либо измените свой код так, чтобы вы выполняли поиск по ключу, а не по значению.

Ответ №5:

Я думаю, что ответы о словаре великолепны, но я думаю, что более широкий ответ заключается в том, чтобы обрабатывать этот материал на уровне базы данных, а не загружать миллионы записей для повторения с использованием словаря в C #. Почему бы не использовать табличный параметр (я предполагаю, что вы используете SQL Server 2008) для передачи данных, которые вы хотите сравнить, и посмотреть, существуют ли они? Вы бы передали его в сохраненную процедуру или что-то еще, что выполняло бы сравнение всех на стороне SQL. Вы могли бы даже сделать что-то вроде:

 INSERT ProductRegistrations
SELECT * FROM @tvpProductsToAdd pa WHERE
pa.firstName   pa.lastName   pa.companyName NOT IN
(SELECT firstName   lastName   companyName FROM ProductRegistrations)
  

@tvpProductsToAdd — это параметр с табличным значением, который вы передаете в своих новых продуктах. Возможно, вам захочется создать какой-то индекс для этих полей, чтобы ускорить сравнение, учитывая, что у вас, похоже, нет ключей, которые вы могли бы сравнить.