Task vs ValueTask

Elemar Júnior

A classe Task/Task<T> simplificou consideravelmente a programação assíncrona com .NET. Entretanto, sendo uma classe, tem suas instâncias alocadas na heap e pode sobrecarregar o Garbage Collector em cenários onde é utilizada no caminho crítico. Daí, o surgimento da estrutura ValueTask/ValueTask<T>.

Entendendo o problema que motivou ValueTask<T>

O uso mais comum para a classe Task é permitir que executemos operações mais pesadas em “segundo plano” enquanto a aplicação se mantem responsiva. Ou seja, para operações com duração mais longa, podemos iniciar uma Task (que irá rodar em outra thread) e “esperar para continuar” assim que a execução desta task estiver completa. Enquanto a task não “retorna”, podemos executar outras operações.

Entretanto, há situações em que nem todas as chamadas para um método potencialmente lento, serão lentas. Considere, por exemplo, o código abaixo, extraído de um post excelente do Stephen Toub. Nesse código, usamos uma Task para preencher um array que serve como buffer para a leitura de um stream byte a byte.

public async Task<int> ReadNextByteAsync()
{
    if (_bufferedCount == 0)
    {
        await FillBuffer();
    }
 
    if (_bufferedCount == 0)
    {
        return -1;
    }
 
    _bufferedCount--;
    return _buffer[_position++];
}

Neste código, o preenchimento do buffer é uma operação custosa e assíncrona (sendo executada como uma Task). Entretanto, perceba que, na maior parte das vezes, as leituras ocorrem a partir de um array e são instantâneas.

No código de exemplo, se o tamanho de _buffer for, por exemplo, 1024 bytes, então, estaremos criando 1023 objetos Tasks na heap sem necessidade(afinal, não há nada a ser executado em “segundo plano”).

Entendendo a solução encontrada

A ideia do time da Microsoft para lidar com cenários onde métodos podem ser, eventualmente lentos, mas são frequentemente rápidos, foi criar o tipo ValueTask<T>.

ValueTask<T> é uma struct que funciona wrapper tanto para um valor do tipo T (resultado do processamento da Task) quanto para uma Task<T>.

public async ValueTask ReadNextByteAsync()
{
    if (_bufferedCount == 0)
    {
        await FillBuffer();
    }

    if (_bufferedCount == 0)
    {
        return -1;
    }

    _bufferedCount--;
    return _buffer[_position++];
}

ValueTask é uma struct, por isso, fica armazenada na stack e não pressiona o GC. No código acima, somente serão instanciados objetos do tipo Task em situações em que uma chamada a FillBuffer for executada.

As vezes síncrono, as vezes assíncrono

Outro uso comum para ValueTask é em abstrações que podem ou não ser custosas.

interface IPotentialyAsync
{
    ValueTask<int> FooAsync();
}

class AsyncImpl : IPotentialyAsync
{
    public ValueTask<int> FooAsync()
    {
        return new ValueTask<int>(Task.Run(/* Potentialy slow func */));
        //;Task.Run( () => 10;/*.. */);
    }
}

class SyncImpl : IPotentialyAsync
{
    public ValueTask<int> FooAsync()
    {
        return new ValueTask<int>(42);
    }
}

No código de exemplo, mostramos como ValueTask<T> permite implementações síncronas e assíncronas sem que objetos desnecessários sejam alocados.

Quando um método assíncrono é muito rápido

Por fim, em muitos casos, podemos estar usando um método assíncrono que pode retornar rapidamente (não justificando a criação de uma Task).

public async ValueTask<int> Foo()
{
    var fee = FeeAsync();

    var feeResult = fee.IsCompletedSuccessfully 
        ? fee.Result 
        : await fee;

    var result = /* SOME COMPUTATION USING feeResult */

    return result;
}

Nesses casos, ValueTask nos permite concluir a execução de maneira síncrona reduzindo, mais uma vez, o impacto sobre o GC.

Por que não usar ValueTask sempre?

O que foi dito até aqui pode estar lhe levando a conclusão de que devemos usar ValueTask sempre. Entretanto, esse não é o caso.

Devemos usar ValueTask somente em cenários onde os ganhos de performance forem fáceis de demonstrar. Afinal, o uso de ValueTask deixa nosso código potencialmente mais difícil de escrever e manter.

ValueTask foi uma das inovações que permitiu ganho de performance assombroso do Kestrel nos últimos tempos. Entretanto, seu uso poderá não fazer efeito positivo algum na performance de sua aplicação (podendo até gerar efeito contrário).

ValueTask é perfeita para cenários onde um método consegue operar de forma síncrona na maior parte das vezes, de forma assíncrona eventualmente, e é executado com muita frequência. Em qualquer outro cenário, não é indicada.

O código gerado pelo compilador quando usamos await com objetos ValueTask tem performance pior do que aquele gerado quando usamos await com objetos Task. Nesses casos, o prejuízo de performance somente será compensado em cenários onde o código é executado com extrema frequência (hotpath).

ValueTask não pode ser utilizada para operações avançadas (de composição, por exemplo). Nesses casos, ela precisaria ser convertida em Task usando o método AsTask o que acabaria neutralizando os ganhos de usar ValueTask.

Concluindo

ValueTask foi uma das inovações da Microsoft para ganhar performance no Kestrel e é mais uma demonstração dos impactos do GC na execução de aplicações .NET. Entretanto, seu uso somente é recomendado para cenários bem específicos.

Dúvidas? Considerações? Compartilhe suas impressões conosco nos comentários.

Compartilhe este insight:

Comentários

Participe deixando seu comentário sobre este artigo a seguir:

Subscribe
Notify of
guest
1 Comentário
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
José Silva
José Silva
1 ano atrás

Obrigado pelo artigo ElemarJr.
Verei no seu canal se há um vídeo sobre isso, acho interessante o assunto.
Gostaria de recomentar a resposta a essa pergunta que fiz ao time do EFCore.
Está no vídeo com título: “Entity Framework Community Standup – Triggers for EF Core”, no tempo 42:59 de 56:34.

O que achou da resposta deles?
Elemar, parabéns pelos conteúdos que está gerando!
Abraços!

AUTOR

Elemar Júnior
Fundador e CEO da EximiaCo atua como tech trusted advisor ajudando empresas e profissionais a gerar mais resultados através da tecnologia.

NOVOS HORIZONTES PARA O SEU NEGÓCIO

Nosso time está preparado para superar junto com você grandes desafios tecnológicos.

Entre em contato e vamos juntos utilizar a tecnologia do jeito certo para gerar mais resultados.

Insights EximiaCo

Confira os conteúdos de negócios e tecnologia desenvolvidos pelos nossos consultores:

Arquitetura de Dados

Insights de um DBA na análise de um plano de execução

Especialista em performance de Bancos de Dados de larga escala
Arquitetura de Software

Estratégias para modernização do legado

Desenvolvedor .NET/NodeJs e especialista em Kafka com experiência em startups e grandes empresas
Infraestrutura e Nuvem

Migração para a nuvem, mais do que mudança tecnológica, implica em mudança da cultura organizacional

Engenheiro de nuvem, arquiteto de software e especialista em Containers e Devops

Acesse nossos canais

Simplificamos, potencializamos e aceleramos resultados usando a tecnologia do jeito certo

EximiaCo 2022 – Todos os direitos reservados

1
0
Queremos saber a sua opinião, deixe seu comentáriox
()
x

Task vs ValueTask

Para se candidatar nesta turma aberta, preencha o formulário a seguir:

Condição especial de pré-venda: R$ 14.000,00 - contratando a mentoria até até 31/01/2023 e R$ 15.000,00 - contratando a mentoria a partir de 01/02/2023, em até 12x com taxas.

Tenho interesse nessa capacitação

Para solicitar mais informações sobre essa capacitação para a sua empresa, preencha o formulário a seguir:

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

O seu insight foi excluído com sucesso!

O seu insight foi excluído e não está mais disponível.

O seu insight foi salvo com sucesso!

Ele está na fila de espera, aguardando ser revisado para ter sua publicação programada.

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

Tenho interesse nessa solução

Se você está procurando este tipo de solução para o seu negócio, preencha este formulário que um de nossos consultores entrará em contato com você:

Tenho interesse neste serviço

Se você está procurando este tipo de solução para o seu negócio, preencha este formulário que um de nossos consultores entrará em contato com você:

× Precisa de ajuda?