Висока производителност съдържа търсене в списък с низове в C#

Имам списък от ок. 500 000 струни, всяка прибл. Дължина 100 знака. При даден термин за търсене искам да идентифицирам всички низове в списъка, които съдържат термина за търсене. В момента правя това с обикновен стар набор от данни, използвайки метода Select ("MATCH %term%"). Това отнема около 600ms на моя лаптоп. Бих искал да го направя по-бързо, може би 100-200ms.

Какъв би бил препоръчителният подход?

Производителността е критична, така че мога да променя отпечатъка на паметта за по-добра производителност, ако е необходимо (в разумни граници). Списъкът с низове няма да се промени, след като бъде инициализиран, така че изчисляването на хешовете също би било опция.

Някой има ли препоръка и кои C# структури от данни са най-подходящи за задачата?


person njr101    schedule 29.09.2011    source източник
comment
Тази задача изглежда доста подходяща за паралелно изпълнение. Колко процесорни ядра имаш? Това плюс Boyer Moore трябва да даде достатъчно добро подобрение.   -  person leppie    schedule 29.09.2011
comment
Опитахте ли да заредите в база данни и да оставите двигателя на базата данни да прави това, в което е добре?   -  person Mongus Pong    schedule 29.09.2011
comment
Търсите ли думи или произволни поднизове?   -  person Gabe    schedule 29.09.2011
comment
MongusPong: Нямам база данни и не мисля, че това ще бъде по-бързо. DB не може да се възползва от индексирането поради клаузата LIKE %...%. Търся оптимизация по поръчка. Гейб: Произволни поднизове. Ако търсех думи, бих могъл да токенизирам низовете и да създам свой собствен индекс.   -  person njr101    schedule 30.09.2011
comment
@leppie Защо BM? Има множество алгоритми за търсене на низове (Aho-Corasick, Wu-Manber) или опити.   -  person Konrad Rudolph    schedule 30.09.2011
comment
@KonradRudolph: Би било просто и само еднократна настройка (или някой от другите класически алгоритми за търсене на низ). Не познавам другите, които споменахте, но опитите със сигурност биха били по-неефективни поради сложността и че не са наистина подходящи за търсене на един низ.   -  person leppie    schedule 30.09.2011
comment
Protip (IIRC, .NET 1.1): Regex ще се опита да търси Boyer Moore за прости модели, напр. foobar.   -  person leppie    schedule 30.09.2011
comment
@leppie Съжалявам, погрешно предположих, че OP се нуждае от търсене с множество шаблони.   -  person Konrad Rudolph    schedule 01.10.2011
comment
Можете ли да споделите окончателния си вариант? @nrj101 ?   -  person Daniel    schedule 27.09.2018


Отговори (7)


Чувал съм добри неща за Lucene.NET, когато става въпрос за извършване на бързо търсене в пълен текст . Те са свършили работата, за да разберат най-бързите структури от данни и такива за използване. Бих предложил да опитате.

В противен случай можете просто да опитате нещо подобно:

var matches = list.AsParallel().Where(s => s.Contains(searchTerm)).ToList();

Но вероятно няма да ви свали до 100ms.

person StriplingWarrior    schedule 29.09.2011
comment
Благодаря за предложението. Lucene изглежда като доста големи режийни разходи (по отношение на рамката) само за тази една ситуация, но мога да го разгледам. Знаете ли дали обработва произволни поднизове? S търсене в свободен текст няма да помогне, ако токенизира низовете в думи. - person njr101; 30.09.2011
comment
@njreed.myopenid.com: Актуализирах отговора си въз основа на моите коментари по друг въпрос. Като се имат предвид обаче изискванията, вероятно ще се нуждаете от промяна на алгоритъма, за да получите подобрението на производителността, което търсите. - person StriplingWarrior; 30.09.2011
comment
Благодаря @StriplingWarrior. Търсенето вече е намалено до около 50 ms. Не е необходимо да прилагам някакъв специален алгоритъм. Комбинацията от използване на Linq със списък от низове беше достатъчна. Предполагам, че наборът от данни носи повече режийни разходи, отколкото си мислех. - person njr101; 13.10.2011

Опитвали ли сте следното?

list.FindAll(x => x.Contains("YourTerm")).ToList();

По някаква причина List.AsParallel().Where(...) е по-бавен от list.FindAll(...) на моя компютър.

list.AsParallel().Where(x => x.Contains("YourTerm")).ToList();

Надявам се това да ви помогне.

person Fever    schedule 13.11.2013

Trie или дърво на суфиксите би помогнало това да стане по-бързо - това е по същество търсенето в пълен текст (обикновено ) използва.

Има реализации в C#, които можете да използвате, вижте също тази SO тема: Търся за прилагането на дървото на суфикса в C#?

Освен това, както беше споменато от @leppie, паралелното изпълнение вероятно вече ще ви осигури увеличението на производителността x3, което търсите. Но след това отново ще трябва да измервате внимателно, без това никой не може да гадае.

person BrokenGlass    schedule 29.09.2011
comment
Благодаря за предложението. Ще видя колко усилване дава паралелното изпълнение и след това ще видя дали имам нужда от промяна в изпълнението. - person njr101; 30.09.2011

Опитвали ли сте да заредите своите низове в List<string> и след това да използвате метода Linq extensions Contains?

var myList = new List<string>();
//Code to load your list goes here...

var searchTerm = "find this";
var match = myList.Contains(searchTerm);
person Paige Cook    schedule 29.09.2011
comment

Опитахте ли да използвате CompareValidator?

Това ви позволява да сравнявате 2 полета за въвеждане и е стандартна контрола според валидаторите Requiredfield и Range.

<asp:CompareValidator ControlToCompare="text1" ControlToValidate="text2" ErrorMessage="error" runat="server" Operator="LessThan" Type="Integer" />
- person RandomEngy; 29.09.2011
comment
Да, за да паснете на OP, ще трябва да направите myList.Where(s => s.Contains(searchTerm)). Бихте могли да хвърлите AsParallel там, за да накарате заявката да се изпълнява паралелно, и можете да промените параметрите на string.Contains, ако знаете, че искате точно съвпадение вместо съвпадение, съобразено с културата. Въпреки това няма да получите порядък на разликата в скоростта. - person StriplingWarrior; 29.09.2011
comment
Добро предложение Stripling. Паралелната обработка може просто да ми даде тласъка, от който се нуждая. Трябва да опитам това. - person njr101; 30.09.2011
comment
Благодаря @Paige-Cook, използването на списък от низове беше чудесно предложение; много по-бързо е. Търсенето вече е намалено до около 50 ms. Мога да приема само един отговор, така че приех Stripling, защото включва използването на Linq за запитване към списъка, както се изисква във въпроса. - person njr101; 13.10.2011

public static bool ContainsFast<T>(this IList<T> list, T item)
{
    return list.IndexOf(item) >= 0;
}

Въз основа на тестовете, които направих, този вариант на Contains беше с около 33% по-бърз от моя страна.

person SuperOli    schedule 29.09.2011
comment
Вероятно беше по-бързо само защото правеше точно сравнение, а не чувствително към културата. - person Gabe; 30.09.2011
comment
Той също така не проверява за поднизове, което е изискване на OP. - person StriplingWarrior; 30.09.2011
comment
Какво е ContainsFast<>? - person Suncat2000; 11.01.2019

Според тези сравнителни показатели, най-бързият начин да проверите дали даден низ се среща в низ е следният:

for (int x = 0; x < ss.Length; x++)
    for (int y = 0; y < sf.Length; y++
        c[y] += ((ss[x].Length - ss[x].Replace(sf[y], String.Empty).Length) / sf[y].Length > 0 ? 1 : 0);

По този начин бихте могли:

  1. Преминете през списъка с помощта на конструкция Parallel.For
  2. Приложете кода по-горе, за да проверите дали даден низ съдържа това, което търсите. "SS" е низът [] от низове за търсене; "SF" е низът [] от низове за търсене; c[y] е общият брой на всяко намерено.

Очевидно ще трябва да ги адаптирате към вашия List[string] (или каквато и да е структура от данни, която използвате).

person Community    schedule 18.12.2018

Трябва да опитате да използвате класа на речника. Той е много по-бърз от List, защото е индексирано търсене.

Dictionary<String, String> ldapDocument = new Dictionary<String, String>();
//load your list here
//Sample -> ldapDocument.Add("014548787","014548787");
var match = ldapDocument.ContainsKey(stringToMatch);
person Greg    schedule 18.03.2013
comment
Речниците са страхотни, но се индексират само по точни ключове. Той иска съдържателно търсене. - person DanO; 11.12.2018