데이터 유형에 너무 많은 걱정을하지 않아도 (즉 중복 코드가 너무 많지 않음) 일부 이미지 분석 알고리즘을 구현하려면 자바의 프리미티브 배열에 대한 방문자 패턴을 설정하고 있습니다.자바 퍼포먼스 : 래퍼 클래스는 기본 유형보다 빠릅니까?
아래의 예에서, 나는 방문자
visit
방법의 서명이visit(int, int double)
- 일반적인 유형입니다 원시적 형의
visit
방법의 서명의 두 가지 유형을 정의했다visit(int, int Double)
입니다.
Appart에서 두 방문자가 정확히 동일한 작업을 수행합니다. 내 생각은 권투/unboxing의 비용을 시도하고 측정하는 것이 었습니다.
그래서 나는 여기가 JIT 꽤 영리한 것을 알고 전체 프로그램
public class VisitorsBenchmark {
public interface Array2DGenericVisitor<TYPE, RET> {
void begin(int width, int height);
RET end();
void visit(int x, int y, TYPE value);
}
public interface Array2DPrimitiveVisitor<RET> {
void begin(final int width, final int height);
RET end();
void visit(final int x, final int y, final double value);
}
public static <RET>
RET
accept(final int width,
final int height,
final double[] data,
final Array2DGenericVisitor<Double, RET> visitor) {
final int size = width * height;
visitor.begin(width, height);
for (int i = 0, x = 0, y = 0; i < size; i++) {
visitor.visit(x, y, data[i]);
x++;
if (x == width) {
x = 0;
y++;
if (y == height) {
y = 0;
}
}
}
return visitor.end();
}
public static <RET> RET accept(final int width,
final int height,
final double[] data,
final Array2DPrimitiveVisitor<RET> visitor) {
final int size = width * height;
visitor.begin(width, height);
for (int i = 0, x = 0, y = 0; i < size; i++) {
visitor.visit(x, y, data[i]);
x++;
if (x == width) {
x = 0;
y++;
if (y == height) {
y = 0;
}
}
}
return visitor.end();
}
private static final Array2DGenericVisitor<Double, double[]> generic;
private static final Array2DPrimitiveVisitor<double[]> primitive;
static {
generic = new Array2DGenericVisitor<Double, double[]>() {
private double[] sum;
@Override
public void begin(final int width, final int height) {
final int length = (int) Math.ceil(Math.hypot(WIDTH, HEIGHT));
sum = new double[length];
}
@Override
public void visit(final int x, final int y, final Double value) {
final int r = (int) Math.round(Math.sqrt(x * x + y * y));
sum[r] += value;
}
@Override
public double[] end() {
return sum;
}
};
primitive = new Array2DPrimitiveVisitor<double[]>() {
private double[] sum;
@Override
public void begin(final int width, final int height) {
final int length = (int) Math.ceil(Math.hypot(WIDTH, HEIGHT));
sum = new double[length];
}
@Override
public void visit(final int x, final int y, final double value) {
final int r = (int) Math.round(Math.sqrt(x * x + y * y));
sum[r] += value;
}
@Override
public double[] end() {
return sum;
}
};
}
private static final int WIDTH = 300;
private static final int HEIGHT = 300;
private static final int NUM_ITERATIONS_PREHEATING = 10000;
private static final int NUM_ITERATIONS_BENCHMARKING = 10000;
public static void main(String[] args) {
final double[] data = new double[WIDTH * HEIGHT];
for (int i = 0; i < data.length; i++) {
data[i] = Math.random();
}
/*
* Pre-heating.
*/
for (int i = 0; i < NUM_ITERATIONS_PREHEATING; i++) {
accept(WIDTH, HEIGHT, data, generic);
}
for (int i = 0; i < NUM_ITERATIONS_PREHEATING; i++) {
accept(WIDTH, HEIGHT, data, primitive);
}
/*
* Benchmarking proper.
*/
double[] sumPrimitive = null;
double[] sumGeneric = null;
double aux = System.nanoTime();
for (int i = 0; i < NUM_ITERATIONS_BENCHMARKING; i++) {
sumGeneric = accept(WIDTH, HEIGHT, data, generic);
}
final double timeGeneric = System.nanoTime() - aux;
aux = System.nanoTime();
for (int i = 0; i < NUM_ITERATIONS_BENCHMARKING; i++) {
sumPrimitive = accept(WIDTH, HEIGHT, data, primitive);
}
final double timePrimitive = System.nanoTime() - aux;
System.out.println("prim = " + timePrimitive);
System.out.println("generic = " + timeGeneric);
System.out.println("generic/primitive = "
+ (timeGeneric/timePrimitive));
}
}
, 그래서 모두 방문자가 동일하게 수행 밝혀졌다 때 너무 놀라지 않았다. 더 놀라운 것은 일반 방문자가 이라는 원시적 인보다 약간 빠르게 수행되는 것 같습니다. 이는 예상치 못한 것입니다. 나는 때때로 벤치마킹이 어려울 수 있음을 알고 있으므로, 뭔가 잘못했을 것입니다. 오류를 발견 할 수 있습니까?
도움을 주셔서 감사합니다. 세바스티앙
내가 (JIT 컴파일러가 작업을 할 수 있도록하기 위해) 예열 단계를 설명하는 코드를 업데이트 한 [편집]. 이 결과는 일관되게 1 (0.95 - 0.98) 미만인 결과는 변경되지 않습니다.
Primitive double을 전달하면 스택에 8 바이트를 복사하는 작업이 포함됩니다. Double을 건네 주면 포인터 복사 만됩니다. –
측정 된 작업을 별도의 메서드에 넣고 컴파일 할 때까지 몇 번 실행해야합니다 (10,000/15,000가 좋음). 그런 다음 루프에서 실행하고 측정하십시오. [이 게시물은 읽어야합니다] (http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java). – assylias
테스트를 반복해서 실행하면 차이가 0.99에서 1.06 사이이며 제네릭은 약간 느립니다. –