Digamos que o gargalo do meu programa Java realmente sejam alguns loops apertados para calcular um monte de produtos de ponto vetorial. Sim, fiz o perfil, sim, é o gargalo, sim é significativo, sim é assim que o algoritmo é, sim, executei o Proguard para otimizar o código de bytes, etc.
O trabalho é, essencialmente, produtos escalares. Como em, tenho dois float[50]
e preciso calcular a soma dos produtos em pares. Eu sei que os conjuntos de instruções do processador existem para realizar esse tipo de operação rapidamente e em massa, como SSE ou MMX.
Sim, provavelmente posso acessá-los escrevendo algum código nativo em JNI. A ligação do JNI acabou sendo muito cara.
Eu sei que você não pode garantir o que um JIT irá compilar ou não. Alguém já ouviu falar de um código de geração de JIT que usa essas instruções? Em caso afirmativo, há algo sobre o código Java que ajuda a torná-lo compilável dessa maneira?
Provavelmente um "não"; vale a pena perguntar.
fonte
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation
. Você precisará de um programa que execute o método vetorizável vezes suficientes para torná-lo "quente".Respostas:
Então, basicamente, você deseja que seu código seja executado mais rapidamente. JNI é a resposta. Eu sei que você disse que não funcionou para você, mas deixe-me mostrar que você está errado.
Aqui está
Dot.java
:e
Dot.h
:Podemos compilar e executar isso com JavaCPP usando este comando:
Com uma CPU Intel (R) Core (TM) i7-7700HQ a 2,80 GHz, Fedora 30, GCC 9.1.1 e OpenJDK 8 ou 11, recebo este tipo de resultado:
Ou cerca de 2,4 vezes mais rápido. Precisamos usar buffers NIO diretos em vez de arrays, mas o HotSpot pode acessar os buffers NIO diretos tão rápido quanto os arrays . Por outro lado, desenrolar manualmente o loop não fornece um aumento mensurável no desempenho, neste caso.
fonte
Para abordar parte do ceticismo expresso por outros aqui, sugiro que qualquer pessoa que queira provar a si mesma ou outra pessoa use o seguinte método:
Exemplo:
O resultado com e sem o sinalizador (no laptop Haswell recente, Oracle JDK 8u60): -XX: + UseSuperWord: 475,073 ± 44,579 ns / op (nanossegundos por op) -XX: -UseSuperWord: 3376,364 ± 233,211 ns / op
A montagem do hot loop é um pouco demais para formatar e colar aqui, mas aqui está um trecho (hsdis.so não está formatando algumas das instruções do vetor AVX2, então executei com -XX: UseAVX = 1): -XX: + UseSuperWord (com '-prof perfasm: intelSyntax = true')
Divirta-se atacando o castelo!
fonte
Nas versões HotSpot começando com Java 7u40, o compilador de servidor fornece suporte para autovetorização. De acordo com JDK-6340864
No entanto, isso parece ser verdade apenas para "loops simples" - pelo menos por enquanto. Por exemplo, a acumulação de uma matriz ainda não pode ser vetorizada JDK-7192383
fonte
Aqui está um bom artigo sobre como experimentar as instruções Java e SIMD escritas por meu amigo: http://prestodb.rocks/code/simd/
O resultado geral é que você pode esperar que o JIT use algumas operações SSE no 1.8 (e algumas mais no 1.9). Embora você não deva esperar muito e precise ter cuidado.
fonte
Você pode escrever o kernel OpenCl para fazer a computação e executá-lo a partir de java http://www.jocl.org/ .
O código pode ser executado na CPU e / ou GPU e a linguagem OpenCL também suporta tipos de vetor, portanto, você deve ser capaz de tirar vantagem explicitamente, por exemplo, das instruções SSE3 / 4.
fonte
Dê uma olhada na comparação de desempenho entre Java e JNI para uma implementação ideal de micro-kernels computacionais . Eles mostram que o compilador do servidor Java HotSpot VM suporta a autovetorização usando o paralelismo de nível de superpalavra, que é limitado a casos simples de paralelismo dentro do loop. Este artigo também lhe dará alguma orientação se o tamanho dos dados é grande o suficiente para justificar a rota JNI.
fonte
Suponho que você escreveu esta pergunta antes de descobrir sobre o netlib-java ;-) ele fornece exatamente a API nativa que você precisa, com implementações otimizadas para máquina, e não tem nenhum custo no limite nativo devido à fixação de memória.
fonte
Eu não acredito que a maioria das VMs sejam inteligentes o suficiente para esse tipo de otimizações. Para ser justo, a maioria das otimizações é muito mais simples, como mudar em vez de multiplicação quando é uma potência de dois. O projeto mono introduziu seu próprio vetor e outros métodos com apoios nativos para ajudar no desempenho.
fonte