JVM의 JIT 컴파일러는 벡터화된 부동소수점 명령을 사용하는 코드를 생성합니까?
자바 프로그램의 병목현상은 벡터 도트 제품군을 계산하기 위한 엄격한 루프입니다.네, 프로파일링했습니다.네, 병목현상입니다.중요하죠.알고리즘이 그렇습니다.바이트 코드를 최적화하기 위해 Proguard를 실행하고 있습니다.
그 작업은 본질적으로 도트 제품이다.저는 두 개 요, 두 가지, 두 개가 있어요.float[50]쌍으로 된 제품의 합계를 계산해야 합니다. 이러한 대량으로하기 위해 합니다.SSE MMX는 SSE나 MMX와 같이 존재합니다.
네, JNI에서 네이티브 코드를 작성하면 접근할 수 있을 것 같습니다.JNI 통화는 꽤 비싼 것으로 나타났습니다.
JIT가 무엇을 컴파일할지, 컴파일하지 않을지는 장담할 수 없다는 것을 알고 있습니다.JIT가 이러한 명령을 사용하는 코드를 생성한다는 것을 들어본 적이 있습니까?만약 그렇다면 자바 코드에 대해 이런 식으로 해석할 수 있는 것이 있습니까?
아마 "아니오"라고 물어볼 가치가 있을 겁니다.
즉, 기본적으로 코드 실행 속도를 향상시키고자 하는 것입니다.JNI가 정답입니다.당신이 그게 당신에게 효과가 없다고 말한 건 알지만, 당신이 틀렸다는 걸 보여줄게.
있습니다Dot.java:
import java.nio.FloatBuffer;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
@Platform(include = "Dot.h", compiler = "fastfpu")
public class Dot {
static { Loader.load(); }
static float[] a = new float[50], b = new float[50];
static float dot() {
float sum = 0;
for (int i = 0; i < 50; i++) {
sum += a[i]*b[i];
}
return sum;
}
static native @MemberGetter FloatPointer ac();
static native @MemberGetter FloatPointer bc();
static native @NoException float dotc();
public static void main(String[] args) {
FloatBuffer ab = ac().capacity(50).asBuffer();
FloatBuffer bb = bc().capacity(50).asBuffer();
for (int i = 0; i < 10000000; i++) {
a[i%50] = b[i%50] = dot();
float sum = dotc();
ab.put(i%50, sum);
bb.put(i%50, sum);
}
long t1 = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
a[i%50] = b[i%50] = dot();
}
long t2 = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
float sum = dotc();
ab.put(i%50, sum);
bb.put(i%50, sum);
}
long t3 = System.nanoTime();
System.out.println("dot(): " + (t2 - t1)/10000000 + " ns");
System.out.println("dotc(): " + (t3 - t2)/10000000 + " ns");
}
}
★★★★★★★★★★★★★★★★★」Dot.h:
float ac[50], bc[50];
inline float dotc() {
float sum = 0;
for (int i = 0; i < 50; i++) {
sum += ac[i]*bc[i];
}
return sum;
}
다음 명령을 사용하여 JavaCPP를 사용하여 컴파일 및 실행할 수 있습니다.
$ java -jar javacpp.jar Dot.java -exec
인텔(R) Core(TM) i7-7700 탑재HQ CPU @ 2.80GHz, Fedora 30, GCC 9.1.1 및 OpenJDK 8 또는 11의 출력은 다음과 같습니다.
dot(): 39 ns
dotc(): 16 ns
또는 약 2.4배 더 빠릅니다.어레이 대신 직접 NIO 버퍼를 사용해야 하지만 HotSpot은 어레이만큼 빠르게 직접 NIO 버퍼에 액세스할 수 있습니다.한편, 이 경우 루프를 수동으로 언롤해도 퍼포먼스가 현저하게 향상되지 않습니다.
여기서 다른 사람들이 표현한 회의론의 일부에 대처하기 위해, 나는 자신이나 다른 사람들에게 증명하고 싶은 모든 사람에게 다음과 같은 방법을 사용할 것을 제안한다.
- JMH 프로젝트 생성
- 벡터화 가능한 수학의 작은 조각을 쓰세요.
- 벤치마크를 -XX:-UseSuperWord와 -XX 사이에서 플립합니다.+UseSuperWord(기본값)
- 성능 차이가 관찰되지 않으면 코드가 벡터화되지 않았을 수 있습니다.
- 확실하게 하기 위해 어셈블리를 출력할 수 있도록 벤치마크를 실행합니다.Linux 에서는 perfasm profiler('-prof perfasm')를 사용하여 원하는 명령이 생성되는지 확인할 수 있습니다.
예:
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE) //makes looking at assembly easier
public void inc() {
for (int i=0;i<a.length;i++)
a[i]++;// a is an int[], I benchmarked with size 32K
}
플래그를 사용한 경우와 사용하지 않은 경우(최근 Haswell 노트북에서는 Oracle JDK 8u60): -XX:+UseSuperWord: 475.073 ± 44.579ns/op (op당 나노초) -XX:-UseSuperWord: 3376.364 ± 233.211 ns/op
핫루프용 어셈블리는 포맷과 고정에 다소 번거롭지만 여기 단편(hsdis)이 있습니다.일부 AVX2 벡터 명령 포맷에 실패했기 때문에 -XX로 실행했습니다.UseAVX=1): -XX:+SuperWord 사용('-prof perfasm:intelSyntax=true' 포함)
9.15% 10.90% │││ │↗ 0x00007fc09d1ece60: vmovdqu xmm1,XMMWORD PTR [r10+r9*4+0x18]
10.63% 9.78% │││ ││ 0x00007fc09d1ece67: vpaddd xmm1,xmm1,xmm0
12.47% 12.67% │││ ││ 0x00007fc09d1ece6b: movsxd r11,r9d
8.54% 7.82% │││ ││ 0x00007fc09d1ece6e: vmovdqu xmm2,XMMWORD PTR [r10+r11*4+0x28]
│││ ││ ;*iaload
│││ ││ ; - psy.lob.saw.VectorMath::inc@17 (line 45)
10.68% 10.36% │││ ││ 0x00007fc09d1ece75: vmovdqu XMMWORD PTR [r10+r9*4+0x18],xmm1
10.65% 10.44% │││ ││ 0x00007fc09d1ece7c: vpaddd xmm1,xmm2,xmm0
10.11% 11.94% │││ ││ 0x00007fc09d1ece80: vmovdqu XMMWORD PTR [r10+r11*4+0x28],xmm1
│││ ││ ;*iastore
│││ ││ ; - psy.lob.saw.VectorMath::inc@20 (line 45)
11.19% 12.65% │││ ││ 0x00007fc09d1ece87: add r9d,0x8 ;*iinc
│││ ││ ; - psy.lob.saw.VectorMath::inc@21 (line 44)
8.38% 9.50% │││ ││ 0x00007fc09d1ece8b: cmp r9d,ecx
│││ │╰ 0x00007fc09d1ece8e: jl 0x00007fc09d1ece60 ;*if_icmpge
성을 습격하는 즐거운 시간 보내세요!
Java 7u40으로 시작하는 HotSpot 버전에서는 서버 컴파일러가 자동 벡터화를 지원합니다.JDK-6340864에 따름
그러나 이는 적어도 현재로서는 "단순 루프"에 대해서만 해당되는 것으로 보인다.예를 들어 어레이 축적은 아직 JDK-7192383에서는 벡터화할 수 없습니다.
여기 내 친구가 작성한 Java 및 SIMD 명령어 실험에 대한 좋은 기사가 있습니다: http://prestodb.rocks/code/simd/
그 결과, JIT는 1.8의 SSE 연산(및 1.9의 연산)을 일부 사용할 수 있습니다.하지만 당신은 많은 것을 기대해서는 안 되고 조심해야 합니다.
OpenCl 커널을 작성하여 컴퓨팅을 수행하고 Java http://www.jocl.org/에서 실행할 수 있습니다.
코드는 CPU 및/또는 GPU에서 실행할 수 있습니다.또한 OpenCL 언어는 벡터 타입을 지원하므로 SSE3/4 명령 등 명시적으로 활용할 수 있습니다.
컴퓨팅 마이크로커널의 최적의 구현을 위해 Java와 JNI의 퍼포먼스 비교를 참조하십시오.이는 Java HotSpot VM 서버 컴파일러가 Super-Word Level Parallelism을 사용한 자동 벡터화를 지원한다는 것을 보여줍니다.이것은 루프 병렬화 내부의 단순한 경우에 한정됩니다.이 문서에서는 데이터 크기가 JNI 루트를 사용할 수 있을 만큼 충분한지 여부도 설명합니다.
netlib-java에 대해 알기 전에 이 질문을 작성하셨을 것입니다.-) 이것은 머신에 최적화된 구현과 함께 필요한 네이티브 API를 제공합니다.메모리 핀 접속으로 인해 네이티브 경계에서 비용이 발생하지 않습니다.
Java 16은 벡터 API(JEP 417, JEP 414, JEP 338)를 도입했습니다.현재는 누구나 사용할 수 있지만, 「삽입」(베타)하고 있습니다.Java 19 또는 20에서는 GA가 될 것입니다.
조금 장황하지만, 신뢰할 수 있고 휴대하기 쉽도록 되어 있습니다.
다음 코드를 다시 작성할 수 있습니다.
void scalarComputation(float[] a, float[] b, float[] c) {
assert a.length == b.length && b.length == c.length;
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
벡터 API 사용:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorComputation(float[] a, float[] b, float[] c) {
assert a.length == b.length && b.length == c.length;
int i = 0;
int upperBound = SPECIES.loopBound(a.length);
for (; i < upperBound; i += SPECIES.length()) {
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
새로운 빌드(Java 18)는 술어 명령어를 사용하여 루프의 마지막 부분을 제거하려고 하지만, 그 지원은 아직 미미한 것으로 생각됩니다.
VM이 이런 종류의 최적화에 충분히 스마트하다고는 생각하지 않습니다.공정하게 말하면, 대부분의 최적화는 2의 거듭제곱이 될 때 곱하는 대신 이동하는 것과 같이 훨씬 단순하다.모노 프로젝트에서는 성능을 향상시키기 위해 네이티브 배경의 자체 벡터 및 기타 방법을 도입했습니다.
언급URL : https://stackoverflow.com/questions/10784951/do-any-jvms-jit-compilers-generate-code-that-uses-vectorized-floating-point-ins
'programing' 카테고리의 다른 글
| phpmyadmin "가져올 데이터가 없습니다" 오류, 수정 방법 (0) | 2023.01.17 |
|---|---|
| CodeIgniter - 뷰에서 $config 변수에 액세스합니다. (0) | 2023.01.17 |
| Python에서는 오브젝트가 반복 가능한지 어떻게 판단합니까? (0) | 2023.01.17 |
| Enter 키를 누른 후 onChange 이벤트를 호출하다 (0) | 2023.01.17 |
| 팬더에서 숫자 열을 찾으려면 어떻게 해야 하나요? (0) | 2023.01.17 |