2010-06-03 3 views
4

Как сделать эту функцию более эффективной. Он работает в течение 6 - 45 секунд. Я пропустил профайлер dotTrace по этому конкретному методу, и это общее время в пределах от 6000 мс до 45 000 мс. Большая часть времени тратится на вызовы «MoveNext» и «GetEnumerator».Рефакторинг - увеличение скорости

и примером времен являются

71.55% CreateTableFromReportDataColumns - 18, 533* ms - 190 calls 
-- 55.71% MoveNext - 14,422ms - 10,775 calls 

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

private static DataTable CreateTableFromReportDataColumns(Report report) 
    { 
     DataTable table = new DataTable(); 
     HashSet<String> colsToAdd = new HashSet<String> { "DataStream" }; 
     foreach (ReportData reportData in report.ReportDatas) 
     { 
      IEnumerable<string> cols = reportData.ReportDataColumns.Where(c => !String.IsNullOrEmpty(c.Name)).Select(x => x.Name).Distinct(); 

      foreach (var s in cols) 
      { 
       if (!String.IsNullOrEmpty(s)) 
        colsToAdd.Add(s); 
      } 
     } 

     foreach (string col in colsToAdd) 
     { 
      table.Columns.Add(col); 
     } 

     return table; 
    } 

Если вам нужны определения таблицы SQL здесь они:

ReportData

ReportID   int 

ReportDataColumn

ReportDataColumnId int 
ReportDataId  int 
Name    varchar(255)  
Value    text  
+2

Почему метод называется 190 раз? Это называется ненужным (вы могли бы назвать это раньше и кэшировать результаты)? Это 190 отчетных отчетов, которые можно обрабатывать параллельно? – Greg

+1

Да, это 190 отдельных отчетов, которые я объединяю в одну таблицу. Каждый отчет может иметь разные имена столбцов, но есть много перекрывающихся имен. –

+0

Просто, чтобы быть ясным, это Linq2Sql или что-то в этом роде? И какую версию .NET вы используете? –

ответ

4

Я считаю, что вы должны быть в состоянии упростить вашу функцию в нечто вроде этого

var columnsToAdd = report.ReportDatas 
        .SelectMany(r => r.ReportDataColumns) 
        .Select(rdc => rdc.Name) 
        .Distinct() 
        .Where(name => !string.IsNullOrEmpty(name)); 

И оттуда добавить имена в таблицу.

+0

как насчет удаления этого нулевого теста из 'where' и тестирования его после выделения? –

+0

Это может быть хорошим предложением, если есть много дубликатов. Я переведу его. –

+0

Приятный и короткий, я хотел бы услышать (от OP), что профайлер думает об этом. –

3

Ваш код (только) запускает петли foreach, поэтому вывод, что метод тратит большую часть своего времени в MoveNext() и др., Не так уж и удивляет.

Вы выполняете двойную работу как с isnullOrEmpty, так и с Distinct (повторяется HashSet).

Моя версия будет:

private static DataTable CreateTableFromReportDataColumns(Report report) 
{ 
    DataTable table = new DataTable(); 
    HashSet<String> colsToAdd = new HashSet<String> { "DataStream" }; 
    foreach (ReportData reportData in report.ReportDatas) 
    { 

     foreach (var column in reportData.ReportDataColumns) 
     { 
      if (!String.IsNullOrEmpty(column.Name)) 
       colsToAdd.Add(column.Name); 
     } 
    } 

    foreach (string col in colsToAdd) 
    { 
     table.Columns.Add(col); 
    } 

    return table; 
} 

Но я не ожидаю, огромное улучшение

+0

Это лидирует до сих пор с 18,428ms за 187 звонков –

0
  • проверка string.isnullorempty повторяется
  • вы можете избавиться от Foreach годов, делая SelectMany (я вижу, что Anthony просто разместил то же самое :)
  • , чтобы сохранить ту же семантику для столбца DataStream (после переключения на версию Anthony), вы могли бы создать новый HashS et (columnsToAdd) {«DataStream»}, но может быть проще или быстрее просто добавить (через concat или union или что-то еще) строку «DataStream», а затем Distinct() и избежать создания HashSet (может также быть профиль как)

это может быть излишним для этого (в зависимости от количества записей в ReportDatas, число столбцов в каждой ReportDataColumns, количество ядер на хост-машине, и т.д.), но вы можете также потенциально распараллеливание.

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

+0

Джеймс, что проблема «DataStream» лучше всего решить с помощью простого if/then на конечных DataColumns. –

0

Это может быть небольшое улучшение по коду Хэнкса. Он использует тот факт, что HashSet скажет вам, была ли операция добавления успешной или элемент уже существовал.

private static DataTable CreateTableFromReportDataColumns(Report report) 
{ 
    HashSet<string> uniqueNames = new HashSet<string> { null, "", "DataStream" }; 

    DataTable table = new DataTable(); 
    table.Columns.Add("DataStream"); 

    foreach (ReportData reportData in report.ReportDatas) 
    { 
     foreach (var dataColumn in reportData.ReportDataColumns) 
     { 
      if (uniqueNames.Add(dataColumn.Name)) 
      { 
       table.Columns.Add(dataColumn.Name); 
      } 
     } 
    } 

    return table; 
} 

Edit: Я пошел вперед и добавил утратившим «» в хэш набора в начале, так что мы больше не нужны для проверки нулевой или пустой.

1

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

private static DataTable CreateTableFromReportDataColumns(Report report) 
{ 
    DataTable table = new DataTable(); 
    table.Columns.Add("DataStream"); 
    IEnumerable<string> moreColumns = report.ReportDatas 
     .SelectMany(z => z.ReportDataColumns) 
     .Select(x => x.Name) 
     .Where(s => s != null && s != "") 
     .Distinct(); 

    foreach (string col in moreColumns) 
    { 
     table.Columns.Add(col); 
    } 

    return table; 
} 

Также возьмите запрос, выданный с использованием профилировщика sql. Затем проанализировать IO и время запроса, запустив его с этими утверждениями, прежде чем

SET STATISTICS TIME ON 
SET STATISTICS IO ON 
    --your query here 

Наконец, вам может потребоваться индекс или два, чтобы принести IO вниз. Здесь важна последовательность столбцов.

CREATE INDEX IX1_ReportData ON ReportData(ReportID, Id) 
CREATE INDEX IX1_ReportDataColumn ON ReportDataColumn(ReportDataId, Name) 
Смежные вопросы