C # асинхронная / ожидание эффективности (или злоупотребления) при чтении DbDataReader - PullRequest
0 голосов
/ 16 января 2019

Наткнулся на относительно часто используемый фрагмент кода, который поначалу казался неэффективным.(Я знаю, что оптимизация иногда бывает злой, но мне было интересно)

вводная часть - довольно простое выполнение SP + чтение возвращаемых данных:

try
{
    await connection.OpenAsync();
    using (var command = connection.CreateCommand())
    {
        command.CommandText = sql.ToString();
        command.Parameters.AddRange(sqlParameters.ToArray());

        var reader = await command.ExecuteReaderAsync();
        if (reader.HasRows)
        {
            while (await reader.ReadAsync())
            {
                 var item = await GetProjectElement(reader);
                 list.Add(item);
            }
         }

         reader.Dispose();
     }      
}
finally
{
    connection.Close();
}

Меня беспокоит функция

await GetProjectElement (reader)

private async Task<Project> GetProjectElement(DbDataReader reader)
{
    var item = new Project
    {
        Id = await reader.GetFieldValueAsync<int>(1),
        ParentId = await reader.IsDBNullAsync(2) ? default(int?) : await reader.GetFieldValueAsync<int>(2),
        Name = await reader.IsDBNullAsync(3) ? default(string) : await reader.GetFieldValueAsync<string>(3),
        Description = await reader.IsDBNullAsync(4) ? default(string) : await reader.GetFieldValueAsync<string>(4),
        Address = await reader.IsDBNullAsync(5) ? default(string) : await reader.GetFieldValueAsync<string>(5),
        City = await reader.IsDBNullAsync(6) ? default(string) : await reader.GetFieldValueAsync<string>(6),
        PostalCode = await reader.IsDBNullAsync(7) ? default(string) : await reader.GetFieldValueAsync<string>(7),
        Type = (ProjectTypeEnum)(await reader.GetFieldValueAsync<byte>(8)),
        StartDate = await reader.IsDBNullAsync(9) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(9),
        EstimatedEndDate = await reader.IsDBNullAsync(10) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(10),
        ActualEndDate = await reader.IsDBNullAsync(11) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(11),
        WebsiteUrl = await reader.IsDBNullAsync(12) ? default(string) : await reader.GetFieldValueAsync<string>(12),
        Email = await reader.IsDBNullAsync(13) ? default(string) : await reader.GetFieldValueAsync<string>(13),
        PhoneNumber = await reader.IsDBNullAsync(14) ? default(string) : await reader.GetFieldValueAsync<string>(14),
        MobilePhoneNumber = await reader.IsDBNullAsync(15) ? default(string) : await reader.GetFieldValueAsync<string>(15),
        Key = await reader.IsDBNullAsync(16) ? default(Guid?) : await reader.GetFieldValueAsync<Guid>(16),
        OrganizationElementId = await reader.GetFieldValueAsync<int>(17),
        CompanyOrganizationElementId = await reader.IsDBNullAsync(18) ? default(int?) : await reader.GetFieldValueAsync<int>(18),
        IsArchived = await reader.GetFieldValueAsync<bool>(20),
        IsDeleted = await reader.GetFieldValueAsync<bool>(21),
        CreatedOn = await reader.GetFieldValueAsync<DateTime>(22),
        CreatedBy = await reader.GetFieldValueAsync<string>(23),
        ModifiedOn = await reader.IsDBNullAsync(24) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(24),
        ModifiedBy = await reader.IsDBNullAsync(25) ? default(string) : await reader.GetFieldValueAsync<string>(25)
    };

    return item;
}

как вы можете видеть, существует МНОЖЕСТВО вызовов await, которые компилятор превращает в конечные автоматы, не так ли?

Здесь вы можете найти упрощенную версию того, как сгенерированный компилятором код здесь .Множество GOTO, что означает переключение контекста снова и снова.

Поскольку SP был выполнен без указания CommandBehavior - данные будут в непоследовательном режиме.(вероятно, причина в том, что строка таблицы не должна быть очень большой в байтах для этого случая Project link )


Мои вопросы :

1) это злоупотребление асинхронностью / ожиданием без очевидной причины, потому что данные строки уже буферизированы в памяти, верно?

2) - это Task<Project> чистые издержкив этом сценарии?

3) будет ли этот подход на самом деле иметь худшую производительность по сравнению с одним без await ing


Заключительные мысли : Если я получу вещиправильно, мы хотели бы использовать CommandBehavior.SequentialAccess для больших строк таблицы, где содержимое может превышать разумную длину, что заставляет нас хотеть читать его асинхронно?(например, хранение varbinary (max) или BLOB-объектов)

1 Ответ

0 голосов
/ 30 января 2019

Как уже отмечали другие, GOTO не вызывает переключение контекста и работает довольно быстро.

1) это злоупотребление асинхронностью / ожиданием без очевидной причины, потому что данные строки уже буферизируются в памяти, верно?

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

2) Является ли Задача чистыми накладными расходами в этом сценарии?

Да, если операция на самом деле синхронная. Что является подробностью реализации вашего поставщика ADO.NET.

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

3) будет ли этот подход иметь худшую производительность по сравнению с тем, который не ожидает

Вероятно, нет. Во-первых, влияние на производительность не будет зависеть от того, какая небольшая работа ЦП выполняется для вызова каждого метода и продолжения синхронного выполнения. Любое влияние на производительность вы увидите из-за дополнительных Task<T> экземпляров, брошенных в кучу Gen0 и необходимости сбора мусора. Это причина, по которой теперь ValueTask<T>.

Но даже это влияние на производительность, скорее всего, будет незаметным рядом с вызовом сетевого ввода-вывода на сервер базы данных. Тем не менее, если вы хотите знать штрафы за микро-производительность, Zen of Async - это классика.

...