Идеи производительности (хэшсет C # в памяти и содержит слишком медленно) - PullRequest
5 голосов
/ 23 февраля 2011

У меня есть следующий код

private void LoadIntoMemory()
{
    //Init large HashSet
    HashSet<document> hsAllDocuments = new HashSet<document>();

    //Get first rows from database
    List<document> docsList = document.GetAllAboveDocID(0, 500000);

    //Load objects into dictionary
    foreach (document d in docsList)
    {
        hsAllDocuments.Add(d);
    }

    Application["dicAllDocuments"] = hsAllDocuments;
}

private HashSet<document> documentHits(HashSet<document> hsRawHit, HashSet<document> hsAllDocuments, string query, string[] queryArray)
{
    int counter = 0;
    const int maxCount = 1000;

    foreach (document d in hsAllDocuments)
    {
        //Headline
        if (d.Headline.Contains(query))
        {
            if (counter >= maxCount)
                break;
            hsRawHit.Add(d);
            counter++;
        }

        //Description
        if (d.Description.Contains(query))
        {
            if (counter >= maxCount)
                break;
            hsRawHit.Add(d);
            counter++;
        }

        //splitted query word by word
        //string[] queryArray = query.Split(' ');
        if (queryArray.Count() > 1)
        {
            foreach (string q in queryArray)
            {
                if (d.Headline.Contains(q))
                {
                    if (counter >= maxCount)
                        break;
                    hsRawHit.Add(d);
                    counter++;
                }

                //Description
                if (d.Description.Contains(q))
                {
                    if (counter >= maxCount)
                        break;
                    hsRawHit.Add(d);
                    counter++;
                }
            }
        }

    }

    return hsRawHit;
}       

Сначала я загружаю все данные в хэш-набор (через приложение для последующего использования) - работает нормально - все в порядке, чтобы быть медленным для того, что я делаю.

Будет работать 4.0-фреймворк в C # (невозможно обновить до нового обновления для 4.0 с асинхронным интерфейсом).

Метод documentHits работает довольно медленно на моей текущей установке - учитывая, что это всев памяти.Что я могу сделать, чтобы ускорить этот метод?

Примеры были бы потрясающими - спасибо.

Ответы [ 4 ]

7 голосов
/ 24 февраля 2011

Я вижу, что вы используете HashSet, но вы не используете ни одного из его преимуществ, поэтому вам следует просто использовать List.

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

Одна из возможностей - установить индексы того, какие документы содержат какие пары символов. Если строка query содержит Hello, вы будете искать документы, содержащие He, el, ll и lo.

Вы можете установить Dictionary<string, List<int>>, где ключом словаря являются комбинации символов, а список содержит индексы к документам в вашем списке документов. Конечно, настройка словаря займет некоторое время, но вы можете сосредоточиться на комбинациях символов, которые встречаются реже. Если комбинация символов существует в 80% документов, это довольно бесполезно для устранения документов, но если комбинация символов существует только в 2% документов, это привело к уменьшению 98% вашей работы.

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

Edit:

Это небольшой пример:

public class IndexElliminator<T> {

  private List<T> _items;
  private Dictionary<string, List<int>> _index;
  private Func<T, string> _getContent;

  private static HashSet<string> GetPairs(string value) {
    HashSet<string> pairs = new HashSet<string>();
    for (int i = 1; i < value.Length; i++) {
      pairs.Add(value.Substring(i - 1, 2));
    }
    return pairs;
  }

  public IndexElliminator(List<T> items, Func<T, string> getContent, int maxIndexSize) {
    _items = items;
    _getContent = getContent;
    _index = new Dictionary<string, List<int>>();
    for (int index = 0;index<_items.Count;index++) {
      T item = _items[index];
      foreach (string pair in GetPairs(_getContent(item))) {
        List<int> list;
        if (_index.TryGetValue(pair, out list)) {
          if (list != null) {
            if (list.Count == maxIndexSize) {
              _index[pair] = null;
            } else {
              list.Add(index);
            }
          }
        } else {
          list = new List<int>();
          list.Add(index);
          _index.Add(pair, list);
        }
      }
    }
  }

  private static List<int> JoinLists(List<int> list1, List<int> list2) {
    List<int> result = new List<int>();
    int i1 = 0, i2 = 0;
    while (i1 < list1.Count && i2 < list2.Count) {
      switch (Math.Sign(list1[i1].CompareTo(list2[i2]))) {
        case 0: result.Add(list1[i1]); i1++; i2++; break;
        case -1: i1++; break;
        case 1: i2++; break;
      }
    }
    return result;
  }

  public List<T> Find(string query) {
    HashSet<string> pairs = GetPairs(query);
    List<List<int>> indexes = new List<List<int>>();
    bool found = false;
    foreach (string pair in pairs) {
      List<int> list;
      if (_index.TryGetValue(pair, out list)) {
        found = true;
        if (list != null) {
          indexes.Add(list);
        }
      }
    }
    List<T> result = new List<T>();
    if (found && indexes.Count == 0) {
      indexes.Add(Enumerable.Range(0, _items.Count).ToList());
    }
    if (indexes.Count > 0) {
      while (indexes.Count > 1) {
        indexes[indexes.Count - 2] = JoinLists(indexes[indexes.Count - 2], indexes[indexes.Count - 1]);
        indexes.RemoveAt(indexes.Count - 1);
      }
      foreach (int index in indexes[0]) {
        if (_getContent(_items[index]).Contains(query)) {
          result.Add(_items[index]);
        }
      }
    }
    return result;
  }

}

Тест:

List<string> items = new List<string> {
  "Hello world",
  "How are you",
  "What is this",
  "Can this be true",
  "Some phrases",
  "Words upon words",
  "What to do",
  "Where to go",
  "When is this",
  "How can this be",
  "Well above margin",
  "Close to the center"
};
IndexElliminator<string> index = new IndexElliminator<string>(items, s => s, items.Count / 2);

List<string> found = index.Find("this");
foreach (string s in found) Console.WriteLine(s);

Выход:

What is this
Can this be true
When is this
How can this be
2 голосов
/ 23 февраля 2011

Вы проходите линейно по всем документам, чтобы найти совпадения - это O (n), вы могли бы добиться большего успеха, если бы решили обратную задачу, аналогично тому, как работает полнотекстовый индекс: начните с условий запроса и предварительно обработайте набор документы, которые соответствуют каждому условию запроса - поскольку это может стать сложным, я бы предложил использовать только БД с возможностью полнотекстового ввода, это будет намного быстрее, чем ваш подход.

Кроме того, вы злоупотребляете HashSet - вместо этого просто используйте List и не вставляйте дубликаты - все случаи в documentHits(), которые приводят к совпадению, должны быть исключительными.

0 голосов
/ 24 февраля 2011

Вы не должны проверять каждый документ на всех этапах тестирования!

Вместо этого вам следует перейти к следующему документу после первого успешного теста.

hsRawHit.Add(d);
counter++;

Вы должны добавить продолжить; после counter ++;

hsRawHit.Add(d);
counter++;
continue;
0 голосов
/ 23 февраля 2011

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

A Trie сделает поиск строки намного быстрее.

Здесь есть небольшое объяснение и реализация в конце .

Другая реализация: Класс Trie

...