Будет ли разница в производительности между циклом каждой строки набора данных и одной и той же формой списка набора данных? - PullRequest
1 голос
/ 25 февраля 2012

Мне нужно зациклить каждую строку набора данных 100 раз.

Этот набор данных содержит 1 первичный ключ и еще один строковый столбец.Набор данных имеет 600 тыс. Строк.

Так что в данный момент я зацикливаюсь следующим образом

for (int i = 0; i < dsProductNameInfo.Tables[0].Rows.Count; i++)
 {    
  for (int k = 0; k < dsFull.Tables[0].Rows.Count; k++)
   {
   }
 }

Теперь dsProductNameInfo имеет 100 тыс. Строк, а dsFull имеет 600 тыс.строк.Если я преобразую dsFull в KeyValuePaired список строк и зациклим, чтобы не было разницы в скорости.

Какое решение будет работать быстрее всего?

Спасибо.

C # 4.0 Приложение WPF

Ответы [ 5 ]

2 голосов
/ 25 февраля 2012

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

Я думаю, что было бы лучше сделать это:

// create a class for each type of object you're going to be dealing with
public class ProductNameInformation { ... }
public class Product { ... }

// load a list from a SqlDataReader (much faster than loading a DataSet)
List<Product> products = GetProductsUsingSqlDataReader(); // don't actually call it that :)

// The only thing I can think of where DataSets are better is indexing certain columns.
// So if you have indices, just emulate them with a hashtable:
Dictionary<string, Product> index1 = products.ToDictionary( ... );

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

Реальный вопрос в том, почему такого рода тяжелая обработка не выполняется на уровне базы данных? SQL-серверы гораздо более оптимизированы для этого типа работы. Кроме того, вам, возможно, не нужно делать это на самом деле, почему бы вам не опубликовать исходную проблему, и, возможно, мы поможем вам оптимизировать процесс глубже?

НТН

1 голос
/ 25 февраля 2012

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

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

В таком случае проблема, с которой вы столкнетесь, заключается в том, что если ваш список подмножества продуктов должен быть передан в SQL из .net, вам нужно вызватьSP 100к раз.К счастью, SQL 2008 R2 представил TableTypes.Вы можете определить тип таблицы в вашей базе данных с одним столбцом для хранения идентификатора вашего продукта, чтобы ваш SP принял его в качестве входного параметра, а затем выполнить внутреннее соединение между вашими фактическими таблицами и вашим параметром таблицы. Я использовал это в своемсобственный проект с очень большими наборами данных, и прирост производительности был огромным.

На стороне .net вы можете создать DataTable, соответствующий структуре типа таблицы SQL, а затем передать его в качестве параметра команды при вызове вашего SP (один раз!).

В этой статье показано, как использовать стороны SQL и .net.http://www.mssqltips.com/sqlservertip/2112/table-value-parameters-in-sql-server-2008-and-net-c/

1 голос
/ 25 февраля 2012

Что вы делаете с данными внутри вложенного цикла?

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

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

Если ни один из этих параметров не подходит, вы могли бы повысить производительность, извлекая «полный» набор данных в качестве DataReader и зацикливая его как внешний цикл. Набор данных загружает все данные из SQL в память за одно нажатие. С 600 тыс. Строк это займет много памяти! Принимая во внимание, что DataReader будет сохранять соединение с БД открытым и потоковые строки по мере их чтения. После прочтения строки память будет повторно использована / восстановлена ​​сборщиком мусора.

1 голос
/ 25 февраля 2012

Может быть довольно много вещей, которые можно оптимизировать, не связанных с циклом. Например. уменьшение количества итераций приведет к значительному увеличению при наличии, тело внутреннего цикла выполняется 100k * 600k раз, поэтому исключение одной итерации внешнего цикла приведет к удалению 600k итераций внутреннего (или вы можете переключить внутренний и внешний цикл). цикл, если легче удалить итерации из внутреннего цикла)

Единственное, что вы можете сделать в любом случае, это индексировать только один раз для каждой таблицы:

var productNameInfoRows = dsProductNameInfo.Tables[0].Rows
var productInfoCount = productNameInfoRows.Count;
var fullRows = dsFull.Tables[0].Rows;
var fullCount = fullRows.Count;
for (int i = 0; i < productInfoCount; i++)
 {
  for (int k = 0; k < fullCount; k++)
   {
   }
 }

внутри циклов вы попадете в строки с productNameInfoRows[i] и FullRows[k], что быстрее, чем при использовании длинной руки. Я полагаю, что оптимизация тела может принести больше пользы, чем способ зацикливания. Коллекция. Если, конечно, вы уже не профилировали код и не обнаружили, что фактическое зацикливание является «горлышком бутылки»

РЕДАКТИРОВАТЬ Прочитав ваш комментарий к Марку о том, что вы пытаетесь достичь. Вот как можно это сделать. Стоит отметить, что приведенный ниже алгоритм является вероятностным. То есть есть 1: 2 ^ 32 для двух слов, которые рассматриваются как равные, но на самом деле это не так. Однако это намного быстрее, чем сравнение строк. Код предполагает, что вы сравниваете первый столбец.

//store all the values that will not change through the execution for faster access
 var productNameInfoRows = dsProductNameInfo.Tables[0].Rows;
 var fullRows = dsFull.Tables[0].Rows;
 var productInfoCount = productNameInfoRows.Count;
 var fullCount = fullRows.Count;
 var full = new List<int[]>(fullCount);


 for (int i = 0; i < productInfoCount; i++){
     //we're going to compare has codes and not strings
     var prd = productNameInfoRows[i][0].ToString().Split(';')
               .Select(s => s.GetHashCode()).OrderBy(t=>t).ToArray();
     for (int k = 0; k < fullCount; k++){
         //caches the calculation for all subsequent oterations of the outer loop
         if (i == 0) {
             full.Add(fullRows[k][0].ToString().Split(';')
                      .Select(s => s.GetHashCode()).OrderBy(t=>t).ToArray());
         }
         var fl = full[k];
         var count = 0;
         for(var j = 0;j<fl.Length;j++){
             var f = fl[j];
             //the values are sorted so we can exit early
             for(var m = 0;m<prd.Length && prd[m] <= f;m++){
                 count += prd[m] == f ? 1 : 0;
             }
         }
         if((double)(fl.Length + prd.Length)/count >= 0.6){
             //there's a match
         }
     }
 }

РЕДАКТИРОВАТЬ ваш комментарий побудил меня попробовать еще раз. Код ниже может иметь меньше итераций. Может быть, потому что это зависит от количества совпадений и количества уникальных слов. Множество уникальных слов и множество совпадений для каждого (что потребует много слов в столбце) потенциально приведет к большему количеству итераций. Однако в предположении, что в каждой строке мало слов, это должно привести к значительно меньшему числу итераций. Ваш код имеет сложность N M, это N + M + (соответствует productInfoMatches * fullMatches). Другими словами, последний должен быть почти 99999 * 600k, чтобы иметь больше итераций, чем у вас

//store all the values that will not change through the execution for faster access
var productNameInfoRows = dsProductNameInfo.Tables[0].Rows;
var fullRows = dsFull.Tables[0].Rows;
var productInfoCount = productNameInfoRows.Count;
var fullCount = fullRows.Count;

//Create a list of the words from the product info
var lists = new Dictionary<int, Tuple<List<int>, List<int>>>(productInfoCount*3);
for(var i = 0;i<productInfoCount;i++){
    foreach (var token in productNameInfoRows[i][0].ToString().Split(';')
                          .Select(p => p.GetHashCode())){
        if (!lists.ContainsKey(token)){
            lists.Add(token, Tuple.Create(new List<int>(), new List<int>()));
        }
        lists[token].Item1.Add(i);
    }
}
//Pair words from full with those from productinfo
for(var i = 0;i<fullCount;i++){
    foreach (var token in fullRows[i][0].ToString().Split(';')
                          .Select(p => p.GetHashCode())){
        if (lists.ContainsKey(token)){
            lists[token].Item2.Add(i);
        }
    }
}

//Count all matches for each pair of rows
var counts = new Dictionary<int, Dictionary<int, int>>();
foreach(var key in lists.Keys){
    foreach(var p in lists[key].Item1){
        if(!counts.ContainsKey(p)){
            counts.Add(p,new Dictionary<int, int>());
        }
        foreach(var f in lists[key].Item2){
            var dic = counts[p];
            if(!dic.ContainsKey(f)){
                dic.Add(f,0);
            }
            dic[f]++;
        }
    }
}
1 голос
/ 25 февраля 2012

Если производительность является критическим фактором, то я бы предложил использовать массив структур;это имеет минимальную косвенность (DataSet / DataTable имеет довольно много косвенных ссылок).Вы упоминаете KeyValuePair, и это сработает, хотя это не обязательно будет мой первый выбор.Милиметрик прав, когда говорит, что сначала создаются DataSet, а , а затем строят массив / список из tht, однако даже тогда экономия времени при зацикливании может превысить время сборки.Если вы можете реструктурировать нагрузку, чтобы полностью удалить DataSet, прекрасно.

Я бы также внимательно посмотрел на циклы, чтобы посмотреть, может ли что-нибудь уменьшить реальную необходимую работу;например, позволит ли создание словаря / группировки ускорить поиск?Разрешает ли сортировка бинарный поиск?Могут ли какие-либо операции быть агрегированными и применяться на более высоком уровне (с меньшим количеством строк)?

...