Рефакторинг - увеличение скорости - PullRequest
4 голосов
/ 03 июня 2010

Как я могу сделать эту функцию более эффективной. В настоящее время он работает на 6 - 45 секунд. Я запустил dotTrace profiler для этого конкретного метода, и его общее время составляет от 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    

Ответы [ 5 ]

4 голосов
/ 03 июня 2010

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

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

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

3 голосов
/ 03 июня 2010

Ваш код (только) выполняет циклы 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;
}

Но я не ожидаю значительного улучшения

1 голос
/ 04 июня 2010

Вы должны были упомянуть 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. Затем проанализируйте ввод-вывод и время запроса, выполнив его с этими операторами до

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

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

CREATE INDEX IX1_ReportData ON ReportData(ReportID, Id)
CREATE INDEX IX1_ReportDataColumn ON ReportDataColumn(ReportDataId, Name)
0 голосов
/ 04 июня 2010

Это может быть небольшим улучшением кода Хэнка. Он использует тот факт, что 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;
}

Редактировать: Я пошел дальше и добавил ноль и "" в хэш-набор в начале, поэтому нам больше не нужна проверка на ноль или пустоту.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...