2016-01-05 2 views
1

Я пытаюсь создать веб-приложение MongoDB с шаблоном IRepository (C#, MVC5), чтобы упростить модульный тест. Просто интересно, может ли кто-нибудь дать мне информацию о том, почему это происходит быстрее.MongoDB C# Driver: API vs Linq Performance

Использование новейших драйверов для MongoDB C#.

В моем IRepository классе я следующий

IQueryable<T> SearchFor(); 

List<T> SearchFor(FilterDefinition<T> filter); 

Нашли SO пост, который предложил использовать IQueryable для скорости по сравнению с использованием IEnumerable.

Вот код от класса MongoRepository.

public IQueryable<T> SearchFor() { 
    return _collection.AsQueryable<T>(); 
} 

public List<T> SearchFor(FilterDefinition<T> filter) { 
    return _collection.Find(filter).ToList(); 
} 

Насколько я могу судить, определение фильтра - это то, как вы обычно кодируете запрос в БД.

Вот вызовы, чтобы получить данные из БД

IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>"); 

FilterDefinition<Client> filter1 = Builders<Client>.Filter.Eq("ClientDesc", "<search text>"); 
List<Client> asd10 = collection.SearchFor(filter1).ToList<Client>(); 

Пожалуйста, обратите внимание, что я знаю, что, вероятно, следует использовать маршрут IQueryable и Linq только для чистого факта, что IRepository не должна содержать технологии зависимую classess (например, FilterDefinition).

При тестировании на коллекцию с 30k простыми документами и тестирование скорости различных методов я получаю следующие результаты.

Использование IQueryable завершается в 3 мс, а FilterDefinition завершается в 43 мс.

Мне интересно, почему запрос Linq на IQueryable быстрее, чем использование API для отправки запроса только для возврата определенного значения?


UPDATE: Из предложения по @lenkan я добавил для каждого цикла для IQueryable.

public void PerformanceTest(IRepository<Client> collection) { 
    Stopwatch sw = new Stopwatch(); 

    // Delete all records 
    // ****************** 
    System.Diagnostics.Debug.WriteLine("*****************"); 

    sw.Start(); 
    collection.DeleteAll(); 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Deleting all records: " + sw.Elapsed); 


    // Create 30k Records 
    // ****************** 
    System.Diagnostics.Debug.WriteLine("*****************"); 

    sw.Reset(); 
    sw.Start(); 
    // Create 30k records 
    for (int i = 0; i < 30000; i++) { 
     Client testclient = new Client() { 
      ClientDesc = "hahahahahahahahah " + i 
     }; 
     collection.Add(testclient); 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Created: 30k rows: " + sw.Elapsed); 


    // Test IQueryable & LINQ 
    // ********************** 
    System.Diagnostics.Debug.WriteLine("*********************"); 
    System.Diagnostics.Debug.WriteLine("* IQueryable & LINQ *"); 
    System.Diagnostics.Debug.WriteLine("*********************"); 

    sw.Reset(); 
    sw.Start(); 
    IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 10"); 
    foreach (Client item in asd4) { 
     string aaaaaa = item.ClientDesc; 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed); 

    sw.Reset(); 
    sw.Start(); 
    IQueryable<Client> asd7 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 10"); 
    foreach (Client item in asd7) { 
     string aaaaaa = item.ClientDesc; 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed); 

    sw.Reset(); 
    sw.Start(); 
    IQueryable<Client> asd5 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 29999"); 
    foreach (Client item in asd5) { 
     string bbbbbb = item.ClientDesc; 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Find one from end: " + sw.Elapsed); 

    sw.Reset(); 
    sw.Start(); 
    for (int i = 10000; i < 10050; i++) { 
     IQueryable<Client> asd6 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah " + i); 
     foreach (Client item in asd6) { 
      string aaaaaa = item.ClientDesc; 
     } 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Find in loop of 50: " + sw.Elapsed); 


    // Test Filter & LINQ 
    // *********************** 
    System.Diagnostics.Debug.WriteLine("*****************"); 
    System.Diagnostics.Debug.WriteLine("* List & Filter *"); 
    System.Diagnostics.Debug.WriteLine("*****************"); 

    sw.Reset(); 
    sw.Start(); 
    FilterDefinition<Client> filter1 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah 10"); 
    List<Client> asd10 = collection.SearchFor(filter1).ToList<Client>(); 
    foreach (Client item in asd10) { 
     string aaaaaa = item.ClientDesc; 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed); 

    sw.Reset(); 
    sw.Start(); 
    FilterDefinition<Client> filter2 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah 29999"); 
    List<Client> asd11 = collection.SearchFor(filter2).ToList<Client>(); 
    foreach (Client item in asd11) { 
     string cccccc = item.ClientDesc; 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Find one from end: " + sw.Elapsed); 

    sw.Reset(); 
    sw.Start(); 
    for (int i = 10000; i < 10050; i++) { 
     FilterDefinition<Client> filter3 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah " + i); 
     List<Client> asd12 = collection.SearchFor(filter3).ToList<Client>(); 
    } 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Find in loop of 50: " + sw.Elapsed); 

    // Delete all records 
    // ****************** 
    System.Diagnostics.Debug.WriteLine("*****************"); 

    sw.Start(); 
    collection.DeleteAll(); 
    sw.Stop(); 
    System.Diagnostics.Debug.WriteLine("Deleting all records: " + sw.Elapsed); 

} 

Вот результаты сейчас. Так что, похоже, перечисляя с IQueryable имеет начальный удар в исполнении, но тогда все, кажется, ускорить, когда вы перезвоните позже ищет то

***************** 
Deleting all records: 00:00:00.0670336 
***************** 
Created: 30k rows: 00:00:04.6829844 
********************* 
* IQueryable & LINQ * 
********************* 
Find one from start: 00:00:00.0878309 
Find one from start: 00:00:00.0120098 
Find one from end: 00:00:00.0116334 
Find in loop of 50: 00:00:00.5890532 
***************** 
* List & Filter * 
***************** 
Find one from start: 00:00:00.0248407 
Find one from end: 00:00:00.0118345 
Find in loop of 50: 00:00:00.5377828 
***************** 
Deleting all records: 00:00:00.7029368 
+1

Интересно. Можете ли вы предоставить минимальный образец кода, который вы использовали для бенчмаркинга? Из предоставленных вами вызовов вы никогда не перечисляете asd4 IQueryable. – lenkan

+0

Добавлен ответ на исходное сообщение. – Dwiea

+0

Я по-прежнему вижу большой бонус от использования Linq вместо FilterDefinitions в качестве метода для удаления определенных классов технологий из интерфейса. Несмотря на крошечный успех в производительности (в основном это будет админ-сервер). – Dwiea

ответ

5

Ваш первоначальный вопрос был почему LINQ был намного быстрее, чем при использовании API. Ответ на этот вопрос связан с тем, что LINQ отложен (ленивый), и запрос на самом деле не выполнялся. Запрос не будет выполнен, пока вы не попытаетесь выполнить итерацию результата (foreach/.ToList()/etc).

Вы, вероятно, приуроченная это заявление:

IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>"); 

, когда вы должны приурочили это заявление:

List<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>").ToList(); 

Цифры производительности вы показываете во время вашего обновления, кажется разумным. LINQ на самом деле немного медленнее, чем использование прямого API, поскольку он добавляет абстракции к запросу. Эта абстракция позволит вам легко изменить MongoDB для другого источника данных (SQL Server/Oracle/MySQL/XML/etc) без большого количества изменений кода, но вы платите за эту абстракцию с небольшим поражением производительности.