Os vetores de indexação no MATLAB são ineficientes?

fundo

Minha pergunta é motivada por observações simples, que de certa forma minam as crenças / suposições frequentemente realizadas / feitas por usuários experientes do MATLAB:

O MATLAB é muito bem otimizado quando se trata das funções integradas e dos recursos fundamentais da linguagem, como vetores de indexação e matrizes.Os loops no MATLAB são lentos (apesar do JIT) e geralmente devem ser evitados se o algoritmo puder ser expresso de maneira nativa, 'vetorizada'.

A linha de fundo: a funcionalidade central do MATLAB é eficiente e tentar superá-lo usando o código MATLAB é difícil, se não impossível.

Investigando o desempenho da indexação vetorial

Os códigos de exemplo mostrados abaixo são tão fundamentais quanto possível: atribuo um valor escalar a todas as entradas de vetor. Primeiro, eu aloco um vetor vaziox:

tic; x = zeros(1e8,1); toc
Elapsed time is 0.260525 seconds.

Tendox Eu gostaria de definir todas as suas entradas para o mesmo valor. Na prática, você faria isso de forma diferente, por exemplo,x = value*ones(1e8,1), mas o ponto aqui é investigar o desempenho da indexação vetorial. A maneira mais simples é escrever:

tic; x(:) = 1; toc
Elapsed time is 0.094316 seconds.

Vamos chamá-lo de método 1 (do valor atribuído ax). Parece ser muito rápido (pelo menos mais rápido que a alocação de memória). Como a única coisa que faço aqui é operar na memória, posso estimar a eficiência desse código calculando olargura de banda de memória efetiva e comparando-o com olargura de banda de memória de hardware do meu computador:

eff_bandwidth = numel(x) * 8 bytes per double * 2 / time

No acima, eu multiplico por2 porque, a menos que seja usado o fluxo SSE, a configuração de valores na memória requer que o vetor seja lido e gravado na memória. No exemplo acima:

eff_bandwidth(1) = 1e8*8*2/0.094316 = 17 Gb/s

Largura de banda da memória STREAM-benchmarked do meu computador é de cerca de 17,9 Gb / s, então de fato - MATLAB oferece desempenho próximo ao pico, neste caso! Por enquanto, tudo bem.

O método 1 é adequado se você deseja definirtodos elementos do vetor para algum valor. Mas se você quiser acessar todos os elementosstep entradas, você precisa substituir o: com por exemplo,1:step:end. Abaixo está uma comparação direta de velocidade com o método 1:

tic; x(1:end) = 2; toc
Elapsed time is 0.496476 seconds.

Embora você não espere que ele seja diferente, o método 2 é claramente um grande problema: desaceleração do fator 5 sem nenhum motivo. Minha suspeita é que, nesse caso, o MATLAB aloca explicitamente o vetor de índice (1:end). Isso é de certa forma confirmado usando o tamanho de vetor explícito em vez deend:

tic; x(1:1e8) = 3; toc
Elapsed time is 0.482083 seconds.

Os métodos 2 e 3 funcionam igualmente mal.

Outra possibilidade é criar explicitamente um vetor de índiceid e usá-lo para indexarx. Isso oferece os recursos de indexação mais flexíveis. No nosso caso:

tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc
Elapsed time is 1.208419 seconds.

Agora isso é realmente algo - desaceleração 12 vezes comparado ao método 1! Eu entendo que deve funcionar pior do que o método 1 por causa da memória adicional usada paraid, mas por que é tão pior do que os métodos 2 e 3?

Vamos tentar dar uma chance aos loops - por mais impossível que pareça.

tic;
for i=1:numel(x)
    x(i) = 5;
end
toc
Elapsed time is 0.788944 seconds.

Uma grande surpresa - um loop bate umvectorized método 4, mas ainda é mais lento do que os métodos 1, 2 e 3. Acontece que, neste caso particular, você pode fazê-lo melhor:

tic;
for i=1:1e8
    x(i) = 6;
end
toc
Elapsed time is 0.321246 seconds.

E esse é provavelmente o resultado mais bizarro deste estudo -um loop escrito em MATLAB supera significativamente a indexação vetorial nativa. Isso certamente não deveria ser assim. Note que o loop JIT'ed ainda é 3 vezes mais lento que o pico teórico quase obtido pelo método 1. Portanto, ainda há muito espaço para melhorias. É apenas surpreendente (uma palavra mais forte seria mais adequada) que a indexação 'vetorizada' usual (1:end) é ainda mais lento.

Questões

é uma indexação simples no MATLABmuito ineficiente (os métodos 2, 3 e 4 são mais lentos que o método 1), ou eu perdi alguma coisa?porque é o método 4(muito) mais devagar que os métodos 2 e 3?por que usar1e8 ao invés denumel(x) como um loop ligado acelerar o código pelo fator 2?

Editar Depois de ler o comentário de Jonas, aqui está outra maneira de fazer isso usando índices lógicos:

tic;
id = logical(ones(1, 1e8));
x(id) = 7;
toc
Elapsed time is 0.613363 seconds.

Muito melhor que o método 4.

Por conveniência:

function test

tic; x = zeros(1,1e8); toc

tic; x(:) = 1; toc
tic; x(1:end) = 2; toc
tic; x(1:1e8) = 3; toc

tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc

tic;
for i=1:numel(x)
    x(i) = 5;
end
toc

tic;
for i=1:1e8
    x(i) = 6;
end
toc

end

questionAnswers(2)

yourAnswerToTheQuestion