2016-11-06 3 views
2

Я написал код для сравнения производительности C# Ado.Net и Entity Framework 6.1.3. Я вызываю хранимую процедуру, которая возвращает около 20 000 записей сотрудников, а затем сопоставляет эти данные в список объектов «Личность». Затем я провел 1000 итераций этого кода и вычислил среднее время.Почему C# Ado.net медленнее, чем Entity Framework 6.1.3?

Вот тайминги:

  • Ado Net: 638 мс

  • Entity Framework: 544 мс

К моему удивлению, Ado Net составляет около 100 мс медленнее, чем Entity Framework.

код Ado.Net:

//GetAllPersons is a stored proc hosted in Local DB instance 
    var adapter = new SqlDataAdapter("GetAllPersons", conn); 
    adapter.Fill(dt); 
    //Using Fast member library 
    var accessor = TypeAccessor.Create(typeof(Person)); 
    MemberSet members = accessor.GetMembers(); 
    var list = new List<Person>(); 
    foreach(DataRow row in dt.Rows) 
    { 
     var person = new Person(); 
     foreach (var member in members) 
     { 
      if (row[member.Name] != DBNull.Value) 
      { 
       accessor[person, member.Name] = row[member.Name]; 
      } 
     } 
     list.Add(person); 
    } 

Entity Framework:

 var context = new AdventureWorks2012Entities1(); 
     List<Person> list = context.GetAllPersons().ToList(); 

Часть кода, который использует SQL Adatper для загрузки Datatable является один принимая большую часть времени. Я попытался использовать SQL Datareader, но это было еще хуже. Я что-то пропустил, потому что предположительно простой Ado.Net должен быть быстрее, чем Entity Framework?

+2

EF основан на Ado.Net, следовательно, он не может быть быстрее, чем чистая реализация Ado.Net. Однако вы реализовали менее идеальное решение с циклом в цикле и другими служебными данными. Я считаю, что внутренняя реализация EF более умна и, возможно, использует инициализацию контекста вместо внутреннего цикла. Эксперимент: иметь только одного сотрудника в таблице и сравнивать. –

+1

'DataAdapter.Fill (DataTable)' очень медленно. Перепишите код на использование ручного сопоставления - это намного быстрее. –

+0

@QualityCatalyst Да, это имеет смысл. Как вы предположили, я запустил код для одного сотрудника, а Ado .Net был примерно на 50% быстрее, чем Entity Framework. Я продолжал увеличивать число сотрудников и достиг порога около 100 сотрудников, когда EF начала принимать участие. – Ketan

ответ

0

Основы Entity Framework (EF) основаны на Ado.Сеть; см. What is Entity Framework? от Entity Framework Tutorial. Следовательно, EF не может быть быстрее, чем чистая реализация Ado.Net.

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

Попробуйте это: у вас есть только один сотрудник в таблице и сравните результаты. EF, вероятно, будет медленнее.

3

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

1. Получите значения столбца по порядку, а не по имени.

Вместо того, чтобы использовать reader["column_name"], вместо этого вы должны получить порядковый номер столбца, а затем использовать его. Например:

using (var reader = command.ExecuteReader()) 
{ 
    int col1Ordinal = reader.GetOrdinal("Column1"); 
    int col2Ordinal = reader.GetOrdinal("Column2"); 
    while (reader.Read()) 
    { 
    int col1 = (int)reader[col1Ordinal]; 
    string col2 = (string)reader[col2Ordinal]; 
    // do something with col1 and col2's values 
    } 
} 

2. Избегайте множественным получает

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

if (row[member.Name] != DBNull.Value) 
{ 
    accessor[person, member.Name] = row[member.Name]; 
} 

Как вы можете видеть, что вы звоните row[member.Name] дважды. Вместо этого, вы должны получить его один раз и повторно использовать значение

object value = row[member.Name]; 
if (value != DBNull.Value) 
{ 
    accessor[person, member.Name] = value; 
} 

3. Избегайте отражения

Я никогда не слышал о TypeAccessor или MemberSet раньше. Из быстрого поиска это похоже на библиотеку под названием fast-member. Даже если это быстрее, чем встроенное отражение .NET, я скептически отношусь к тому, как быстро это происходит. Я знаю, что хорошо, что он может значительно уменьшить количество кода, который вы должны написать, особенно если ваш запрос имеет много столбцов. Но если вы пытаетесь оптимизировать производительность, особенно если вы недовольны работой вашего кода по сравнению с Entity Framework, вы должны удалить эту зависимость и проверить, что такое разница в производительности.

+0

При использовании SQL DataReader я добавлял по одной строке одновременно к Datatable при чтении reader.Read(), потому что я хотел, чтобы мой код работал на переменное число столбцов/разных объектов. На самом деле я не обращал много внимания на то, что многие получают, потому что это уже быстро. Я уже тестировал производительность библиотеки Fast Member и нашел ее примерно на 50% быстрее, чем общий код отражения. – Ketan

+0

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