Por que o Farseer 2.x armazena temporários como membros e não na pilha? (.INTERNET)

10

ATUALIZAÇÃO: Esta pergunta se refere ao Farseer 2.x. O 3.x mais recente não parece fazer isso.

Atualmente, estou usando o Farseer Physics Engine bastante extensivamente e notei que parece armazenar muitos tipos de valor temporário como membros da classe, e não na pilha, como seria de esperar.

Aqui está um exemplo da Bodyclasse:

private Vector2 _worldPositionTemp = Vector2.Zero;

private Matrix _bodyMatrixTemp = Matrix.Identity;
private Matrix _rotationMatrixTemp = Matrix.Identity;
private Matrix _translationMatrixTemp = Matrix.Identity;

public void GetBodyMatrix(out Matrix bodyMatrix)
{
    Matrix.CreateTranslation(position.X, position.Y, 0, out _translationMatrixTemp);
    Matrix.CreateRotationZ(rotation, out _rotationMatrixTemp);
    Matrix.Multiply(ref _rotationMatrixTemp, ref _translationMatrixTemp, out bodyMatrix);
}

public Vector2 GetWorldPosition(Vector2 localPosition)
{
    GetBodyMatrix(out _bodyMatrixTemp);
    Vector2.Transform(ref localPosition, ref _bodyMatrixTemp, out _worldPositionTemp);
    return _worldPositionTemp;
}

Parece que é uma otimização de desempenho manual. Mas não vejo como isso poderia ajudar no desempenho? (Se alguma coisa eu acho que machucaria fazendo objetos muito maiores).

Andrew Russell
fonte

Respostas:

6

Embora no .NET os tipos de valor estejam armazenados na pilha, resultando em um custo mínimo de alocação, ele não elimina o custo de inicialização.

Nesse caso, temos um conjunto de funções usando uma ou duas matrizes temporárias, o que resultaria na inicialização de 16 a 32 pontos flutuantes por chamada. Embora isso possa parecer insignificante, se os métodos forem usados ​​com bastante frequência (digamos, milhares e milhares de vezes por quadro), a sobrecarga total pode ter um impacto significativo. Se essa técnica for usada sistematicamente em todos esses métodos, a sobrecarga eliminada poderá ser considerável.

Embora o uso dessa técnica elimine a capacidade de fornecer segurança de rosca em um nível por objeto, geralmente não é aconselhável fornecer essa garantia em um nível granular.

Jason Kozak
fonte
Você tem certeza sobre isso? Um pensamento que eu tinha era que poderia ser para evitar chamar construtores. Mas para tipos de valor, você não precisa chamar um construtor - incluindo o construtor sem parâmetros padrão - se quiser definir todos os membros (ou passá-lo como outparâmetro). Tenho certeza de que todo o objetivo desta regra é que o compilador possa pular o zeramento dessa memória - certo? (É realmente tão lento para mover o ponteiro da pilha?)
Andrew Russell
Surpreendente, não? Infelizmente, se você inspecionar a IL gerada, as matrizes temporárias serão inicializadas. Alguns testes rápidos mostram que a versão de membro-temp é ~ 10-15% mais rápida.
21410 Jason Jasonzaza
1
Estou atordoado . Em "Entendendo o desempenho do XNA Framework" (GDC2008), Shawn Hargreaves diz sobre as estruturas: "[o JIT] geralmente descobre: ​​'na próxima linha, ele define imediatamente todos os três campos [do Vector3], então nem preciso inicializá-lo para zero '" . É daí que minhas informações vêm. Mas ao ouvir novamente agora, ele apenas diz "normalmente". O próximo ponto imediato na apresentação é que o JIT se comporta de maneira diferente com o depurador conectado, afetando o desempenho (como você testou?). Além disso: ele está falando sobre o JIT aqui, então talvez o IL permaneça "agradável" (verificabilidade?).
Andrew Russell
A IL foi inspecionado via refletor, e os testes foram executados fora do IDE construído em Release (no Windows, já não tenho uma adesão CC para teste contra)
Jason Kozak
1
Com base nisso - pergunto-me se seria melhor (e quão melhor seria) tornar esses membros temporários static(e / ou reutilizá-los de forma mais agressiva). Por exemplo, a Bodyclasse em Farseer tem cerca de 73 carros alegóricos no valor de membros "desnecessários".
Andrew Russell
-1

Boa pergunta. Eu sou um cara c # / .NET muito afiado e um pouco maluco por desempenho, e isso me parece uma decisão de design bastante estranha. A primeira coisa que me impressiona é que esse código não é de forma alguma seguro para threads. Não sei se isso é um problema em um sistema de física, mas armazenar dados temporários fora do escopo de um método geralmente é uma receita para um desastre.

Honestamente, se eu encontrasse esse tipo de código regularmente em uma estrutura de terceiros, provavelmente tentaria encontrar outra estrutura.

Mike Strobel
fonte
3
Realmente não responde à pergunta.
22910 Brian Ortiz
Sim, o melhor que pude fazer é confirmar que ele não é louco, e não parece haver nenhum benefício real em ser codificado dessa maneira. A única descoberta da verdadeira intenção é perguntar ao cara que escreveu o código :).
quer
Obrigado, Mike. Estou começando a suspeitar que o desenvolvedor original é o louco, não eu. Mas ele sempre ajuda a verificação;)
Andrew Russell
Às vezes, a segurança do encadeamento pode ser uma garantia dispendiosa, especialmente ao escrever uma biblioteca pesada de computação FP para uma plataforma que não utiliza as instruções do SIMD.
21710 Jason Jasonzaza
-1

O GC no 360 basicamente faz apenas coleções GEN 2, que são caras, então variáveis ​​temporárias que são criadas e removidas a cada quadro (como objetos temporários) fazem com que coleções inteiras sejam executadas, o que prejudica o desempenho muito rápido.

Eu suspeito que eles fizeram dessa maneira para reutilizar esse objeto e não o coletaram.

Steven Evers
fonte
1
Isso ocorreu-me também, mas os membros temporários parecem ser tipos de valor, portanto, eles não seriam alocados no heap gerenciado de qualquer maneira.
Mike Strobel
2
Certo, mas apenas se eles forem alunos. Se eles são locais (com escopo definido pelo método), eles seriam alocados na pilha. A questão é por que eles simplesmente não seguiram esse caminho.
Mike Strobel
1
@Blair - De acordo com o MSDN ( msdn.microsoft.com/en-us/library/bb203912.aspx ), o Xbox360 usa o .NET Compact Framework. Parece que a diferença no GC está relacionada a isso, então eu continuaria com isso para mais pesquisas sobre o assunto.
Logan Kincaid
1
@Blair: @Logan Kincaid está correto. O coletor de lixo do CF se comporta de maneira diferente da estrutura regular. Há uma boa conversa sobre o assunto no XNA Game Studio 3.0 Unleashed - no entanto, esse livro estará desatualizado em breve com o lançamento do 4.0.
Steven Evers
1
@Blair: Devido ao ambiente de memória limitado do 360 (e da maioria dos dispositivos direcionados pelo CF), um GC Mark & ​​Sweep é usado. Como resultado, muitas alocações pequenas acionarão uma coleção e o tempo de coleta é relativo ao número de referências. Muitos detalhes aqui: download.microsoft.com/.../Mobility/…
Jason Kozak