Как выполнить PLINQ существующий запрос LINQ с помощью объединений?

Я использую LINQ для сравнения двух DataSet друг с другом для создания новых строк и обновления Я заметил, что полное сравнение длится ~ 1,5 часа, и только одно из двух ядер занято (диспетчер задач составляет 50-52% загрузки ЦП). Должен признать, что я совершенно новичок в параллельном LINQ , но я предполагаю, что это может значительно повысить производительность.

Итак, мой вопрос: как и что мне распараллеливать?

Это исходные запросы (сокращенные до самого важного):

'check for new data
Dim srcUnique = From row In src.Email_Total
                Select Ticket_ID = row.ticket_id, Interaction = row.interaction, ModifiedAt = row.modified_time

Dim destUnique = From row In dest.ContactDetail
                 Where row.ContactRow.fiContactType = emailContactType.idContactType
                 Select row.ContactRow.Ticket_ID, row.Interaction, row.ModifiedAt

'get all emails(contactdetails) that are in source but not in destination
Dim diffRows = srcUnique.Except(destUnique).ToList

'get all new emails(according to ticket_id) for calculating contact columns
Dim newRowsTickets = (From row In src.Email_Total
                     Join d In diffRows
                     On row.ticket_id Equals d.Ticket_ID _
                     And row.interaction Equals d.Interaction _
                     And row.modified_time Equals d.ModifiedAt
                     Group row By Ticket_ID = row.ticket_id Into NewTicketRows = Group).ToList

For Each ticket In newRowsTickets
     Dim contact = dest.Contact.FindByTicket_IDfiContactType(ticket.Ticket_ID, emailContactType.idContactType)
     If contact Is Nothing Then
          ' Create new Contact with many sub-queries on this ticket(omitted) ****'
          Dim newContact = Me.dest.Contact.NewContactRow
          dest.Contact.AddContactRow(newContact)
          contact = newContact
     Else
          ' Update Contact with many sub-queries on this ticket(omitted) '
     End If
     daContact.Update(dest.Contact)

     ' Add new ContactDetail-Rows from this Ticket(this is the counterpart of the src.Email_Total-Rows, details omitted) '
     For Each newRow In ticket.NewTicketRows
         Dim newContactDetail = dest.ContactDetail.NewContactDetailRow
         newContactDetail.ContactRow = contact
         dest.ContactDetail.AddContactDetailRow(newContactDetail)
     Next
     daContactDetails.Update(dest.ContactDetail)
Next

Примечание : daContact и daContactDetails - это SqlDataAdapters , source и dest - это DataSets и Contact и ContactDetail - это DataTables , где каждый ContactDetail принадлежит Контакт.

Даже если бы не оба ядра использовали 100% ЦП, я предполагаю, что если бы я распараллелил запросы, это значительно увеличило бы производительность, потому что второе ядро ​​почти бездействует. для каждого также может быть хорошим местом для оптимизации, поскольку билеты не связаны друг с другом. Поэтому я предполагаю, что мог бы работать в цикле с несколькими потоками и параллельно создавать / обновлять записи. Но как это сделать с помощью PLINQ?

Боковое примечание : Как я уже упоминал в комментариях, производительность пока не является для меня ключевым фактором, поскольку единственная цель сервера - синхронизировать базу данных MySQL (на другом сервере) с MS SQL- Сервер (на том же сервере, что и этот Windows-Сервис). Он действует как источник отчетов, созданных другой службой. Но эти отчеты создаются только один раз в день. Но помимо этого мне было интересно изучить PLINQ, потому что я подумал, что это может быть отличным упражнением. Упомянутые 1,5 часа требуются только в том случае, если база данных назначения пуста и все записи должны быть созданы. Если обе базы данных почти синхронизированы, этот метод займет всего ~ 1 минуту.В будущем производительность станет более важной, поскольку электронная почта является только одним из нескольких типов контактов (чат + звонки будут превышать 1 миллион записей). Я думаю, что тогда мне все равно понадобится какой-нибудь (LINQ) пейджинг данных.

Если что-то неясно, я соответствующим образом обновлю свой ответ. Заранее спасибо.


Изменить : Вот результат моих исследований и попыток:

Вопрос: Как «PLINQ» существующий запрос LINQ с объединениями?

Ответ : Обратите внимание, что некоторые операторы LINQ являются бинарными - они принимают два объекта IEnumerable в качестве входных данных. Join - прекрасный пример такого оператора. В этих случаях тип крайнего левого источника данных определяет, используется ли LINQ или PLINQ. Таким образом, вам нужно вызвать AsParallel только для первого источника данных, чтобы ваш запрос выполнялся параллельно:

IEnumerable leftData = ..., rightData = ...;
var q = from x in leftData.AsParallel()
        join y in rightData on x.a == y.b
        select f(x, y);

Но если я изменю свой запрос следующим образом (обратите внимание на AsParallel ):

Dim newRowsTickets = (From row In src.Email_Total.AsParallel()
                                        Join d In diffRows
                                        On row.ticket_id Equals d.Ticket_ID _
                                        And row.interaction Equals d.Interaction _
                                        And row.modified_time Equals d.ModifiedAt
                                    Group row By Ticket_ID = row.ticket_id Into NewTicketRows = Group).ToList

Компилятор будет жалуются, что мне также нужно добавить AsParallel в правильный источник данных. Так что, похоже, это проблема VB.NET или отсутствие документации (статья 2007 года). Я предполагаю последнее, потому что в статье (помимо этой рекомендуемой) также говорится, что вам нужно добавить System.Concurrency.dll вручную, но на самом деле он является частью .NET 4.0 Framework и в пространстве имен Sytem. Threading.Tasks .

Я понял, что не получу выгоды от распараллеливания За исключением , поскольку запрос выполняется достаточно быстро в последовательном режиме (даже с почти одинаковым количеством строк в обеих коллекциях, что приводит к максимальному количеству сравнений , Я получил результат менее чем за 30 секунд).Но для полноты картины добавлю позже.

Поэтому я решил распараллелить для каждого , что так же просто, как с LINQ-запросами, вам просто нужно добавить AsParallel () в конце. {{1} } Но я понял, что мне нужно усилить параллелизм с помощью WithExecutionMode (ParallelExecutionMode.ForceParallelism) , иначе .NET решит использовать только одно ядро ​​для этого цикла. Я тоже хотел рассказать.NET, что я хочу использовать как можно больше потоков, но не более 8: WithDegreeOfParallelism (8).

Теперь оба ядра работают одновременно, но загрузка ЦП остается на 54%.

Итак, это версия PLINQ:

Dim diffRows = srcUnique.AsParallel.Except(destUnique.AsParallel).ToList

Dim newRowsTickets = (From row In src.Email_Total.AsParallel()
                        Join d In diffRows.AsParallel()
                        On row.ticket_id Equals d.Ticket_ID _
                        And row.interaction Equals d.Interaction _
                        And row.modified_time Equals d.ModifiedAt
                    Group row By Ticket_ID = row.ticket_id Into NewTicketRows = Group).ToList

For Each ticket In newRowsTickets.
                    AsParallel().
                      WithDegreeOfParallelism(8).
                       WithExecutionMode(ParallelExecutionMode.ForceParallelism)
    '  blah,blah ...  '

    'add new ContactDetails for this Ticket(only new rows)
    For Each newRow In ticket.NewTicketRows.
                                AsParallel().
                                    WithExecutionMode(ParallelExecutionMode.Default)
        ' blah,blah ... '
    Next
    daContactDetails.Update(dest.ContactDetail)
Next

К сожалению, я не вижу никаких преимуществ в производительности от использования AsParallel по сравнению с последовательным режимом:

для каждого ] с AsParallel (чч: мм: сс.мм):

09/29/2011 18:54:36: Contacts/ContactDetails created or modified. Duration: 01:21:34.40

И без:

09/29/2011 16:02:55: Contacts/ContactDetails created or modified. Duration: 01:21:24.50

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


Ниже приведены рекомендуемые материалы для чтения:

13
задан greatwolf 7 October 2011 в 01:00
поделиться