Почему люди говорят, что SqlDataReader.GetXXX (i) быстрее, чем SqlDataReader [i]? - PullRequest
2 голосов
/ 10 февраля 2012

Обновление: оказывается, отражение не должно быть медленным.Используйте Fasterflect (http://www.codeproject.com/Articles/38840/Fasterflect-a-fast-and-simple-API-for-Reflection-i). Это делает отражения буквально (и я имею в виду слово «буквально» в самом буквальном смысле, а не в переносном смысле, как это часто неправильно) в 100 раз быстрее.

Я получил свой код сейчасотметьте, что он загружает данные и помещает данные в мои бизнес-объекты так же быстро, как студия управления SQL-сервером может выполнить выборку * в таблицах.


Я только что запустил этот фрагмент кода, который проверяет любойтипов данных в моей таблице и использует соответствующие методы Get:

        foreach (var p in obj.Properties)
        {
            object value;
            var i = fieldNumbers[p.Alias];

            if (p.Type == "System.Nullable`1[System.Int16]") value = dr.GetSqlInt16(i);
            else if (p.Type == "System.Nullable`1[System.Int32]") value = dr.GetSqlInt32(i);
            else if (p.Type == "System.Nullable`1[System.Decimal]") value = dr.GetSqlDecimal(i);
            else if (p.Type == "System.Nullable`1[System.Boolean]") value = dr.GetSqlBoolean(i);
            else if (p.Type == "System.String") value = dr.GetSqlString(i);
            else if (p.Type == "System.Nullable`1[System.DateTime]") value = dr.GetSqlDateTime(i);
        }

и это:

            foreach (var p in obj.Properties)
            {
                object value;
                var i = fieldNumbers[p.Alias];
                value = dr[i];
            }

и второй последовательно работает быстрее. Я был удивлен этим, но кажетсячтобы быть правдой. Может кто-нибудь сказать мне, если я пропускаю что-то здесь, потому что я видел, как несколько человек утверждают, что методы GetXXX работают лучше. Я рассчитал это в целом, а также время отдельных операций извлечения. Я действительно просто развенчал миф?

РЕДАКТИРОВАТЬ: После тестирования еще несколько я обнаружил пару вещей.

1-й - Это "немного" быстрее (примерно 8% для тестов, которые я выполнил), чтобы использовать метод get, который возвращает значение в строго типизированную переменную, и я проверил это без всего лишнего кода, описанного выше, чтобы не было никакой отправки или чего-то в этом роде ... просто яблоки яблокам.

Однако обратите внимание, что я использую функции GetSqlXXX, а не функции GetXXX.Это потому, что последний не может использоваться для нулевых значений.Тем не менее, первый возвращает типы, такие как SqlInt32, а не int ?.Мои поля не SqlXXX, хотя, это простые обнуляемые типы, такие как int ?.Я думаю, что это часто случается с большинством людей, что означает, что вы на самом деле не получаете увеличение скорости типизированных методов, если только вы не хотите начать работать с SqlTypes во всем вашем коде.

Во-вторых, язаметил, что получение нулевых значений кажется медленнее, чем вы ожидаете в целом ... но это, конечно, мое мнение.

РЕДАКТИРОВАТЬ 2: Только для Дуга МакКлина и TheEvilPenguin я рассчитал "просто" ветвление следующим образом:

        Stopwatch sw = new Stopwatch();
        long time = 0;

        while (dr.Read())
        {
            var obj = new O();
            obj.Initializing = true;


            sw.Restart();            
            foreach (var p in obj.Properties)
            {
                if (p.Type == "System.Nullable`1[System.Int16]") continue;
                else if (p.Type == "System.Nullable`1[System.Int32]") continue;
                else if (p.Type == "System.Nullable`1[System.Decimal]") continue;
                else if (p.Type == "System.Nullable`1[System.Boolean]") continue;
                else if (p.Type == "System.String") continue;
            }
            time += sw.ElapsedTicks;
        }
        sw.Stop();
        MessageBox.Show(time.ToString());

Мне пришлось оставить там пару строк, не относящихся к разветвлению, но вы можете видеть, что я только добавляю время вокруг разветвления.Сначала я делал это за миллисекунды, и результат (для записей около 60 тыс.) Был равен 1. Очевидно, что каждый цикл меньше миллисекунды, поэтому я переключился на тики, и результат составил 466472, что составляет менее 1 половины 1 миллисекунды (если я не получилмои десятичные знаки перепутали ... кто-нибудь, пожалуйста, поправьте меня, если я там).Так как дорого обходится ветвление?Нет.

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

Ответы [ 2 ]

4 голосов
/ 10 февраля 2012

Это все зависит от поставщика ADO, этот ответ касается поставщика SQL Server ADO.NET, также известного как SqlClient.

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

Для действительного микро-теста GetXYZ немного немного немного быстрее, поскольку GetValue имеет немного больше накладных расходов, в частности:

  1. GetValue направляет данные во внутренний SqlBuffer.Value, который является простым оператором case и отправляет те же свойства, что и GetXYZ dispatings.

  2. GetValue вызывает SqlStatistics.StartTimer, тогда как GetXYZ не делает.

Возможно, вы могли бы испечь немного более быструю реализацию GetValue(), я сомневаюсь, что это того стоит.

Следующий микро-тест демонстрирует разницу в производительности:

// include Dapper from nuget
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Dapper;
using System.Diagnostics;

namespace ConsoleApplication16
{
    class Program
    {
        static void Main(string[] args)
        {
            var cnn = new SqlConnection("Data Source=.;Initial Catalog=tempdb;Integrated Security=True");
            cnn.Open();

            cnn.Execute("create table #t(num int, str nvarchar(50))");

            // 10 k records
            cnn.Execute("insert #t values (@num, @str)", 
                Enumerable.Range(1, 10000).Select(i => new { num = i, str = Guid.NewGuid().ToString() }));

            Stopwatch sw;

            SqlCommand cmd = new SqlCommand("select * from #t");
            cmd.Connection = cnn;

            for (int i = 0; i < 10; i++)
            {
                sw = Stopwatch.StartNew();
                using (var reader = cmd.ExecuteReader())
                {
                    int num;
                    string str;
                    while (reader.Read())
                    {
                        num = reader.GetInt32(0);
                        str = reader.GetString(1);
                    }
                }
                Console.WriteLine("GetXYZ {0}", sw.ElapsedTicks);

                sw = Stopwatch.StartNew();
                using (var reader = cmd.ExecuteReader())
                {
                    int num;
                    string str;
                    while (reader.Read())
                    {
                        num = (int)reader.GetValue(0);
                        str = (string)reader.GetValue(1);
                    }
                }
                Console.WriteLine("GetValue {0}", sw.ElapsedTicks);
            }

            Console.ReadKey();
        }
    }
}

Результаты:

GetXYZ 25094
GetValue 27877
GetXYZ 24226
GetValue 25450
...
GetXYZ 24029
GetValue 26571

GetValue постоянно немного медленнее.На 5% хуже в абсолютном наихудшем случае.

3 голосов
/ 10 февраля 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...