Re: JVM vs my VM
This is a multi-part message in MIME format.
--------------020802030002060100020007
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Am 09.01.2010 12:43, schrieb Jon Harrop:
Kevin McMurtrie wrote:
The main problem with Java in the context of performance is lack of value
types. That prevents Java from being competitively performant in many cases
including this one. Hash tables are another obvious example: .NET is 32x
faster.
Sometimes these limitations are a serious hassle so you use C++. In the
case of the ray tracer benchmark, I think an experienced Java coder
could have written it differently without increased effort.
Dozens of experienced Java coders have submitted implementations. What you
see is the fastest of all of them but, as you can see, it is nowhere near
competitively performant.
hmm.. i have just took the
http://www.ffconsultancy.com/languages/ray_tracer/code/5/ray.java and
did some minor improvements (see file attachted) which are mainly simple
refactorings and the code got 8-12% faster.
On the other hand if you do repeated executions (do a loop in the main
program) you see how the JIT kicks in and the succeeding executions are
faster than the first one.
(Funny thing is that JIT cannot optimize your program as good as mine :-))
As you always tell that you try to ignore the Java startup, why not
measure the time within each program?
Cheers,
Markus
--------------020802030002060100020007
Content-Type: text/plain;
name="Raytracer.java"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Raytracer.java"
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
public final class Raytracer {
static final double delta = Math.sqrt(Math.ulp(1.0));
static final double infinity = Float.POSITIVE_INFINITY;
static class Vec {
public double x, y, z;
public Vec(double x2, double y2, double z2) {
x = x2;
y = y2;
z = z2;
}
}
static Vec add(Vec a, Vec b) {
return new Vec(a.x + b.x, a.y + b.y, a.z + b.z);
}
static Vec sub(Vec a, Vec b) {
return new Vec(a.x - b.x, a.y - b.y, a.z - b.z);
}
static Vec scale(double s, Vec a) {
return new Vec(s * a.x, s * a.y, s * a.z);
}
static double dot(Vec a, Vec b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
static double length(Vec a) {
return Math.sqrt(dot(a, a));
}
static Vec unitise(Vec a) {
return scale(1 / length(a), a);
}
static class Ray {
final public Vec orig, dir;
public Ray(Vec o, Vec d) {
orig = o;
dir = d;
}
}
static class Hit {
public double lambda;
public Vec normal;
public Hit(double l, Vec n) {
lambda = l;
normal = n;
}
}
abstract static class Scene {
abstract public void intersect(Hit i, Vec dir);
abstract public boolean sintersect(Ray ray);
abstract public Sphere bound(Sphere b);
}
static class Sphere extends Scene {
final public Vec center;
final public double radius;
public Sphere(Vec c, double r) {
center = c;
radius = r;
}
public double ray_sphere(Vec dir) {
double b = center.x * dir.x + center.y * dir.y + center.z * dir.z;
double disc = b * b - (center.x * center.x + center.y * center.y + center.z * center.z) + radius * radius;
if (disc < 0)
return infinity;
double d = Math.sqrt(disc);
double t2 = b + d;
if (t2 < 0)
return infinity;
double t1 = b - d;
return (t1 > 0 ? t1 : t2);
}
@Override
public void intersect(Hit i, Vec dir) {
double l = ray_sphere(dir);
if (l >= i.lambda)
return;
i.normal.x = l * dir.x - center.x;
i.normal.y = l * dir.y - center.y;
i.normal.z = l * dir.z - center.z;
//double len = Math.sqrt(i.normal.x * i.normal.x + i.normal.y * i.normal.y + i.normal.z * i.normal.z);
double len = length(i.normal);
i.normal.x /= len;
i.normal.y /= len;
i.normal.z /= len;
i.lambda = l;
}
@Override
public boolean sintersect(Ray ray) {
double vx = center.x - ray.orig.x, vy = center.y - ray.orig.y, vz = center.z - ray.orig.z;
double b = vx * ray.dir.x + vy * ray.dir.y + vz * ray.dir.z;
double disc = b * b - (vx * vx + vy * vy + vz * vz) + radius * radius;
return disc >= 0 && b + Math.sqrt(disc) >= 0;
}
@Override
public Sphere bound(Sphere b) {
double s = length(sub(b.center, center)) + radius;
return new Sphere(b.center, (b.radius > s ? b.radius : s));
}
}
static class Group extends Scene {
final public Sphere bound;
final public Scene[] objs;
public Group(Sphere b, Scene[] o) {
bound = b;
objs = o;
}
@Override
public void intersect(Hit i, Vec dir) {
double l = bound.ray_sphere(dir);
if (l >= i.lambda)
return;
for (Scene scene : objs)
scene.intersect(i, dir);
}
@Override
public boolean sintersect(Ray ray) {
if (!bound.sintersect(ray))
return false;
for (Scene scene : objs)
if (scene.sintersect(ray))
return true;
return false;
}
@Override
public Sphere bound(Sphere b) {
for (Scene scene : objs)
b = scene.bound(b);
return b;
}
}
double ray_trace(Vec neg_light, Vec dir, Scene scene) {
Hit i = new Hit(infinity, new Vec(0, 0, 0));
scene.intersect(i, dir);
if (i.lambda == infinity)
return 0;
double g = dot(i.normal, neg_light);
if (g <= 0)
return 0.;
Vec o = add(scale(i.lambda, dir), scale(delta, i.normal));
return (scene.sintersect(new Ray(o, neg_light)) ? 0 : g);
}
Scene create(int level, Vec c, double r) {
Sphere sphere = new Sphere(c, r);
if (level == 1)
return sphere;
double x = 3 * r / Math.sqrt(12);
Scene[] objs = new Scene[1 + 2 * 2];
int index = 0;
objs[index++] = sphere;
Sphere b = new Sphere(add(c, new Vec(0, r, 0)), 2 * r);
for (int dz = -1; dz <= 1; dz += 2) {
for (int dx = -1; dx <= 1; dx += 2) {
Vec c2 = new Vec(c.x + dx * x, c.y + x, c.z + dz * x);
Scene scene = create(level - 1, c2, r / 2);
objs[index++] = scene;
b = scene.bound(b);
}
}
return new Group(b, objs);
}
void run(int n, int level, int ss) throws java.io.IOException {
Scene scene = create(level, new Vec(0, -1, 4), 1);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("image.pgm"));
out.write(("P5\n" + n + " " + n + "\n255\n").getBytes());
Vec constVec = new Vec(1, 3, -2);
int ssSquare = ss * ss;
Vec unitise = unitise(constVec);
int ssInverse = 1 / ss;
int nHalf = n / 2;
for (int y = n - 1; y >= 0; --y)
for (int x = 0; x < n; ++x) {
double g = 0;
for (int dx = 0; dx < ss; ++dx)
for (int dy = 0; dy < ss; ++dy) {
Vec d = new Vec(x + dx * ssInverse - nHalf, y + dy * ssInverse - nHalf, n);
g += ray_trace(unitise, unitise(d), scene);
}
out.write((int) (.5 + 255 * g / ssSquare));
}
out.close();
}
public static void main(String[] args) throws java.io.IOException {
for (int i = 0; i < 10; i++) {
long start = System.nanoTime();
(new Raytracer()).run(Integer.parseInt(args[1]), Integer.parseInt(args[0]), 4);
long end = System.nanoTime();
System.out.println("Raytracer Took: " + (end - start)/1000/1000);
start = System.nanoTime();
(new ray()).run(Integer.parseInt(args[1]), Integer.parseInt(args[0]), 4);
end = System.nanoTime();
System.out.println("Ray Took: " + (end - start)/1000/1000);
}
}
}
--------------020802030002060100020007--