Experiments | Programming

O yes do GNU é mais rápido que o yes do BusyBox?

Olá a todos! Como vão? Espero que estejam bem. Outro dia eu estava investigando o comportamento dos comandos do Linux, o que me levou a notar que o yes do GNU é mais rápido que em outras implementações, como o BusyBox ou o BSD.

O que é o yes?

O comando yes é um utilitário extremamente simples: imprime repetidamente uma string (por padrão y) até que o processo seja interrompido ou a saída falhe. Apesar de sua simplicidade funcional e de seu uso reduzido pela evolução dos programas de terminal, existem implementações com filosofias muito diferentes, como as do GNU coreutils e do BusyBox.

Desta vez, vou conduzir um experimento prático para comparar ambas as implementações, analisando os resultados obtidos e concluindo se a complexidade do yes do GNU se justifica para o uso no mundo real.

Descrição do experimento

Foram realizados dois tipos de testes:

Teste de desempenho bruto (sem terminal)

/bin/yes | pv > /dev/null
busybox yes | pv > /dev/null

/bin/yes é a implementação do GNU, enquanto busybox yes é a implementação do BusyBox.

Esse teste mede quantos dados cada implementação consegue gerar quando a saída não é limitada por um dispositivo lento (como um terminal), mas sim por um pipe e pelo kernel.

Teste prático (saída no terminal)

(sleep 10 && pkill /bin/yes) & /bin/yes
(sleep 10 && pkill busybox) & busybox yes

Neste teste, ambos os comandos escrevem diretamente no terminal por 10 segundos. Conta-se o número aproximado de linhas geradas e observa-se o consumo de memória.

Resultados

Desempenho bruto

Saída do terminal:

ItsZariep@PC~-> /bin/yes | pv > /dev/null
37.7GiB 0:00:10 [3.96GiB/s] [<=>]
ItsZariep@PC~-> busybox yes | pv > /dev/null
991MiB 0:00:07 [99.0MiB/s] [<=>]
  • yes do GNU: vários GiB/s (≈ 3-4 GiB/s)
  • yes do BusyBox: vários MiB/s (≈ 100 MiB/s)

A diferença é de várias ordens de grandeza.

Teste prático no terminal

  • yes do GNU: ~50.120 linhas em 10 segundos
  • yes do BusyBox: ~49.994 linhas em 10 segundos

As linhas foram contadas com precisão graças ao tmux.

A diferença é pequena e quase imperceptível.

Uso de RAM

Segundo o htop, top e btop:

  • yes do BusyBox: 570 KiB (0,5 MiB)
  • yes do GNU: 5900 KiB (5 MiB)

yes do GNU: aproximadamente 10× mais memória que o yes do BusyBox.

Análise técnica

Por que o yes do GNU é tão rápido em pipes

O yes do GNU:

Segundo o código-fonte do GNU coreutils, especificamente:

  /* If a larger buffer was allocated, fill it by repeating the buffer
     contents.  */

  size_t copysize = bufused;
  for (size_t copies = bufalloc / copysize; --copies;)
    {
      memcpy (buf + bufused, buf, copysize);
      bufused += copysize;
    }
  • Constrói um buffer grande (dezenas de KiB) contendo várias repetições da saída.
  • Usa write diretamente.
  • Reduz drasticamente o número de chamadas de sistema ao armazenar em buffer repetições do “y”.

Isso permite um uso extremamente eficiente do kernel e da largura de banda de memória, ao custo de usar mais memória.

Por que o yes do BusyBox é “mais lento”

O yes do BusyBox:

Segundo o código-fonte do BusyBox no GitHub:

	do {
		pp = argv;
		while (1) {
			fputs_stdout(*pp);
			if (!*++pp)
				break;
			putchar(' ');
		}
	} while (putchar('\n') != EOF);
  • Usa stdio (putchar, fputs).
  • Escreve apenas alguns bytes por iteração.
  • Realiza muitas operações de alto nível.

Isso introduz sobrecarga no espaço do usuário e limita o desempenho máximo, apesar de usar menos memória.

O terminal como gargalo

Quando a saída vai para o terminal:

  • Renderização
  • Gerenciamento do scrollback
  • Line discipline

limitam a velocidade a alguns milhares de linhas por segundo. Nesse cenário, nenhuma das otimizações do yes do GNU oferece uma vantagem significativa. (Usar um terminal acelerado como o Kitty também não faz muita diferença; os testes usaram Alacritty, foot e qterminal.)

O yes do GNU é superdimensionado?

Do ponto de vista prático:

  • A maioria dos usos do yes é interativa ou de curta duração.
  • O terminal domina o custo geral.
  • O maior uso de memória não traz benefícios visíveis.

Da filosofia do GNU coreutils:

  • O yes deve ser o mais eficiente possível.
  • Ele nunca deve se tornar o gargalo.
  • Deve ter bom desempenho mesmo em benchmarks sintéticos ou extremos.

Ambas as posições são consistentes com seus respectivos objetivos.

Outras implementações

BSD

int
main(int argc, char *argv[])
{
	if (argc > 1)
		for (;;)
			puts(argv[1]);
	else
		for (;;)
			puts("y");
}
  • É mais simples que o BusyBox e tem desempenho relativamente parecido.
  • Usa puts que, embora tenha um buffer interno no BSD, é mais lento e ainda resulta em mais chamadas de sistema.

UUtils

fn prepare_buffer(buf: &mut Vec<u8>) {
    if buf.len() * 2 > BUF_SIZE {
        return;
    }

    assert!(!buf.is_empty());
    let line_len = buf.len();
    let target_size = line_len * (BUF_SIZE / line_len);
    while buf.len() < target_size {
        let to_copy = std::cmp::min(target_size - buf.len(), buf.len());
        debug_assert_eq!(to_copy % line_len, 0);
        buf.extend_from_within(..to_copy);
    }
}
  • É um port bastante fiel da implementação do GNU.
  • Em vez de imprimir "y" a cada iteração, imprime blocos de 16 KB de uma só vez usando stdout.write_all(bytes).
  • Deveria ser ainda mais rápido em benchmarks sintéticos.

Resultados

Para o uso cotidiano, o yes do BusyBox é suficiente, mais simples e mais eficiente em memória.

O yes do GNU, embora claramente superdimensionado para o caso de uso típico, alcança seu objetivo de maximizar o desempenho em cenários ideais, como pipes rápidos ou testes de estresse.

O experimento demonstra que uma implementação “melhor” depende do contexto: desempenho teórico máximo versus simplicidade e adequação ao mundo real.

Conclusão

Para o yes em cenários do mundo real, a implementação do GNU poderia ser considerada excessiva.

Mas essa complexidade não é um erro; é, antes, uma consequência direta de uma filosofia de projeto diferente.


Avatar

ItsZariep

YouTuber e programador, utiliza Linux desde 2015

Sobre mim


© 2026 ItsZariep

Powered by Tessera for Hugo