2014-09-23 2 views
0

Скажем, у меня есть класс, как это:Как ускорить это где?

class ItemLimits 
{ 
    public string strAccountNumber = string.Empty; 
    public string strUserCode = string.Empty; 
    public long lAccumulatedAmount = 0L; 
} 

У меня есть массив из них с 50000 элементов в нем.

У меня также есть набор данных из 50000 элементов (более или менее), и мне нужно найти элемент в массиве ItemLimits, который соответствует этому набору данных.

Это делается в настоящее время в цикле через набор данных с этим:

ItemLimits ilItemLimit = itemlimits.Where(s => s.strUserCode.Equals(dataset[i].User_UserCode, StringComparison.CurrentCultureIgnoreCase) 
         && s.strAccountNumber.Equals(strUnEditedHomingAccountNo, StringComparison.CurrentCultureIgnoreCase)).First(); 

strUnEditedHomingAccountNo извлекается из набора данных ранее.

Как только я нашел ItemLimit Мне нужно, мне нужно добавить к его lAccumulatedAmount.

Что я видел в тестах производительности, так это то, что это очень быстро, так как цикл начинается, но с течением времени замедляется. Это линейное замедление, которое вы можете видеть на этом графике, который я сделал: time per item in ms

К тому времени, как я достиг ~ 40000 предметов, каждый предмет занимает ~ 40 мс для завершения. Это имеет смысл в моей голове, потому что я предполагаю, что это просто повторение элементов один за другим, пока не найдет совпадение, что, очевидно, довольно медленно с большим количеством предметов.

Количество элементов как в массиве, так и в наборе данных может сильно различаться.

Я думал о попытке сделать заказ массив и делать Array.BinarySearch, но я не знаю, как заказать это наиболее эффективно, учитывая, что strUserCode и strAccountNumber оба могут изменить, и я не могу предсказать порядок набор данных.

Это самая медленная часть программы, поэтому я бы попытался ее оптимизировать (около 70% времени тратится только на это, и происходит много чего другого).

Если бы кто-нибудь мог дать мне несколько указателей на то, что я могу сделать, это было бы очень полезно.

Я использую .NET 3.5, и я не могу это изменить.

+6

Этот вопрос не соответствует теме, потому что он принадлежит https://codereview.stackexchange.com/ – Dirk

+0

К сожалению. Возможно ли перенести вопросы по всем направлениям или я должен удалить их и попросить их? – Logan

+0

Я не знаю, может быть, модератор может это сделать. – Dirk

ответ

1

Я угробил Where целиком и сделать это с помощью Array.BinarySearch с помощью этого компаратора:

class ItemLimitsComparer : Comparer<ItemLimits> 
    { 
    public override int Compare(ItemLimits x, ItemLimits y) 
    { 
     if(Convert.ToInt32(x.strUserCode) < Convert.ToInt32(y.strUserCode)) 
     { 
     return -1; 
     } 
     if(Convert.ToInt32(x.strUserCode) > Convert.ToInt32(y.strUserCode)) 
     { 
     return 1; 
     } 
     if(Convert.ToInt32(x.strUserCode) == Convert.ToInt32(y.strUserCode)) 
     { 
     if(Convert.ToInt64(x.strAccountNumber) < Convert.ToInt64(y.strAccountNumber)) 
     { 
      return -1; 
     } 
     if(Convert.ToInt64(x.strAccountNumber) > Convert.ToInt64(y.strAccountNumber)) 
     { 
      return 1; 
     } 
     if(Convert.ToInt64(x.strAccountNumber) == Convert.ToInt64(y.strAccountNumber)) 
     { 
      return 0; 
     } 
     } 
     return 0; 
    } 
    } 

(это первый раз, когда я использовал это, я подозреваю, что есть ошибка, скрывающаяся где-то)

Where был заменен следующим образом:

int index = Array.BinarySearch(itlaCreditLimits, new ItemLimits { strUserCode = dataset[i].User_UserCode, strAccountNumber = strUnEditedHomingAccountNo }, new ItemLimitsComparer()); 
if(index < 0) 
{ 
    throw new Exception("Didn't find ItemLimit for UserCode = " + dataset.User_UserCode + " and account number " + strUnEditedHomingAccountNo); 
} 

ItemLimits ilItemLimit = itlaCreditLimits[index]; 

Это у меня от 15 минут для всех 50k элементов до 25 сек кунд.

+0

Если вы сначала делаете конвертирование вызовов и сохраняете значения в локальных переменных, вы избегаете некоторых вызовов методов, и я бы утверждал, что можно повысить удобочитаемость. – Zache

+0

@ Zache good catch. Готово. – Logan

0

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

Попробуйте что-то вроде этого, вам нужно будет изменить тип DataSet (понятия не имею о том, что вы используете) и, возможно, решить, как обрабатывать ключевые коллизий при принятии itemLimitDictionary, но кроме того, что он должен ускорить красиво:

public void DoTheNeedfull(ItemLimit[] itemLimits, DataSet dataSet) 
{ 
    var itemLimitDictionary = itemLimits.ToDictionary(i => MakeKey(i.One, i.Two), i => i); 

    for(var i = 0; i < dataSet.Count; i++) 
    { 
     var strUnEditedHomingAccountNo = BlackMagicMethod(dataSet[i]); 
     var key = MakeKey(dataSet[i].User_UserCode, strUnEditedHomingAccountNo); 

     if(itemLimitDictionary.ContainsKey(key)) 
     { 
      // do your thing 
     } 
     else 
     { 
      // maybe this is an error 
     } 
    } 
} 

private string MakeKey(string keyPartOne, string keyPartTwo) 
{ 
    return keyPartOne.ToUpperInvariant() + keyPartTwo.ToUpperInvariant(); 
} 
+0

Я пробовал использовать словарь (я должен был упомянуть об этом), но по какой-то причине мне не удалось изменить часть «Значение». Также lol @ BlackMagicMethod. Я действительно нашел способ выполнить двоичный поиск, и он сменился с 15 минут до 25 секунд на моей машине, используя это. – Logan

0

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

Так что это звучит как задание для выражения linq GroupJoin().С этим вы получите объект ItemLimits со всеми соответствующими наборами данных, как IEnumerable<DataSetItem>. Затем вы можете позвонить Aggregate() по этому внутреннему перечислению и записать эту информацию в данный объект ItemLimits.