Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (132)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2
  ignore  |  Print  
  float[] view of direct FloatBuffer + mapped objects (misery)  (Read 6253 times)
0 Members and 1 Guest are viewing this topic.
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Posted 2007-01-09 16:57:33 »

Wouldn't this be great?


int size = 1024;

FloatBuffer buf = ByteBuffer.allocateDirect( size * Float.SIZE / 8 ).order(ByteOrder.nativeOrder()).asFloatBuffer();
float[] arr = buf.asFloatArray();

ShortBuffer buf = ByteBuffer.allocateDirect( size * Short.SIZE / 8 ).order(ByteOrder.nativeOrder()).asShortBuffer();
short[] arr = buf.asShortArray();


Then we could finally get around the performance-difference and handling between the two.

Is it possible to do this with JNI?

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #1 - Posted 2007-01-10 00:00:21 »

I worked a few hours on it, and it works now!

The float-array is directly mapped to a direct FloatBuffer, so the float[] is basicly in non-heap memory!

No JNI, no bytecode-weaving, and few dirty tricks:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
      FloatPair pair = ArrayBufferTool.createFloatPair(1024);
      FloatBuffer buf = pair.buffer();
      float[] arr = pair.array();

      System.out.println("buf.capacity = " + buf.capacity());
      System.out.println("arr.length = " + arr.length);
      System.out.println();

      System.out.println("buf @ 0 = " + buf.get(0));
      System.out.println("arr @ 0 = " + arr[0]);
      System.out.println();
      buf.put(0, 3.1415f);
      System.out.println("buf @ 0 = " + bug.get(0));
      System.out.println("arr @ 0 = " + arr[0]);
      System.out.println();
      arr[0] = 1.234f;
      System.out.println("buf @ 0 = " + buf.get(0));
      System.out.println("arr @ 0 = " + arr[0]);
      System.out.println();


Output:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
buf.capacity = 1024
arr.length = 1024

buf @ 0 = 0.0
arr @ 0 = 0.0

buf @ 0 = 3.1415
arr @ 0 = 3.1415

buf @ 0 = 1.234
arr @ 0 = 1.234



Note that you can't (ever!) create a float[] for an existing FloatBuffer.

Last but not least: the FloatBuffer will never be GCed, as it is unknown whether float[] is still refering to it.



I hope somebody cares about my performance-topics this time Smiley

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #2 - Posted 2007-01-10 00:24:10 »

As you may have realized, with my API I can map any object to a direct java.nio.Buffer (a float[] is just a regular object)

My structs are almost like C now: except that the object-overhead is embedded in the Buffer


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
class Vec
{
   int x, y, z;
}


// setup the direct 'heap'

int sizeof = MemoryBlock.sizeof(Vec.class); // 3 ints -> 12 bytes + 8 bytes overhead = 20 bytes
int instances = 1024;

ByteBuffer buffer = ...(sizeof * instances);
MemoryBlock block = new MemoryBlock(buffer);


// use a dummy instance to see which type the method should return

Vec dummy = new Vec();
Vec mapped1 = block.map(dummy, 0 * sizeof);
Vec mapped2 = block.map(dummy, 1 * sizeof);



// no performance penalty

mapped1.x = mapped2.y - mapped2.z;
mapped1.y = mapped2.x + mapped2.z;
mapped1.z = mapped2.y - mapped2.x;


// shift pointer, no objects alloced

mapped1 = block.map(mapped1, 43 * sizeof);
mapped2 = block.map(mapped2, 42 * sizeof);


The VM doesn't know better than that those references point to real Objects.

Same here about the GC.. the MemoryBlock will never (!) be GCed, as there could be dozens of Vec being mapped to it.

FYI: the object-overhead cannot be removed from the struct, as the VM needs it to treat a pointer like an Object.


Suggestions? Smiley

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline nva225

Junior Devvie





« Reply #3 - Posted 2007-01-10 05:22:24 »

Heh, congratulations. What you did sounds pretty cool and I'm sure I'd be very excited about it if I coded it or was using it, but I'm afraid I'm not deep enough into array buffer use to fully appreciate what you have here. I don't believe I've seen anything of this type before though.

Good job none-the-less. Smiley
Offline Ken Russell

JGO Coder




Java games rock!


« Reply #4 - Posted 2007-01-10 08:26:44 »

Are you using sun.misc.Unsafe to do these tricks? If so, they will probably work great up until the first garbage collection, at which point the JVM will core dump. You can't do these kinds of operations (casting random pieces of memory to Objects) safely. Work is continuing in HotSpot to make NIO Buffers go faster in all situations so that you can stop using arrays and just use Buffers everywhere in your application.
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #5 - Posted 2007-01-10 08:44:03 »

Infact, I got that crash earlier, but fixed it.

It's not crashing, ever, not with a normal GC, and not with a full GC.



Further, I know that work is being done in this area. I'd hate to be a bit blunt, but after several years, the performance is still not comparable (see my other posts in this sub-forum). And having mapped Objects adds functionality, which is unlikely Sun is going to implement.

If Sun would add mapped objects (and bump buffer-performance), I'd be more than happy to ditch my code, really.


Imagine an API that crunches float[]s, and you only have FloatBuffers laying around, or vice versa. One would have to copy between the datastructures, while with shared resources, you can process the float[] the API expects (or that simply performs faster), work with the FloatBuffer in say LWJGL. (and when doing engine-logic, map the data into Objects for convenience)

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Ken Russell

JGO Coder




Java games rock!


« Reply #6 - Posted 2007-01-10 08:58:52 »

If you'd be willing to post your code, I'd be happy to look at it and point out any potential problems, but in the absence of that I have to warn people on these forums not to get too excited. I don't think there's any possible way this can be done safely given the current primitives in the JVM and Java libraries.
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #7 - Posted 2007-01-10 09:01:20 »

That's the reason I didn't post it right away, 'first feeling the waters'. (is that an expression?)

I'll PM you in a few hours. Got to go to school now Smiley

Java class at 10:40 Grin (about how Swing works, without being taught how object references work!)

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline zero

Junior Devvie





« Reply #8 - Posted 2007-01-10 13:05:56 »

Would it be possible to overload 'MemoryBlock.map', so that I could do:

1  
Vec mapped = block.map(Vec.class, 1*sizeof);


or is the dummy allocation needed?

Signature would be like this:

1  
2  
<T> T MemoryBlock map(Class<T> type, int size) // throws UnsupportedTypeException or whatever ?
{ .. }


Of course creatiing an instance of 'T' with reflection is slow, but this is an one time init thing, right? IMHO it's more elegant - but of course the original method should remain too..
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #9 - Posted 2007-01-10 13:07:29 »

To be honest, the passed argument isn't only for determining type, there are more things going on under the hood.

I need that instance. Wink

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline pepijnve

Junior Devvie




Java games rock!


« Reply #10 - Posted 2007-01-10 13:10:17 »

To be honest, I'm surprised you got this working. Java objects aren't necessarily located at a fixed place in memory. What happens if the GC decides to move the mapped Object to a different place in memory?
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #11 - Posted 2007-01-10 13:43:59 »

I'd really like to see the code behind this, it sounds like a neat hack.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #12 - Posted 2007-01-10 14:11:42 »

To be honest, I'm surprised you got this working. Java objects aren't necessarily located at a fixed place in memory. What happens if the GC decides to move the mapped Object to a different place in memory?

I'm managing my own references (AKA smart pointers). The GC only 'touches' my mapped objects (modifies the object-header every once in a while), but never changes the pointer of my references.

This code:
long pntr = MemoryBlock.getPointer(obj);
always returns the same pointer (in direct memory) after a lot of GCs and full GCs.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline princec

« JGO Spiffy Duke »


Medals: 434
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #13 - Posted 2007-01-10 15:42:32 »

That'd depend on the GC algorithm being used. I believe the compacting GC will bugger it right up.

Perhaps you can pin the objects in JNI using getCritical thingies?

Cas Smiley

Offline pepijnve

Junior Devvie




Java games rock!


« Reply #14 - Posted 2007-01-10 15:47:08 »

I'd really like to see the code behind this, it sounds like a neat hack.
I'll second that.

Perhaps you can pin the objects in JNI using getCritical thingies?
The JNI spec says you're only allowed to use get*Critical for small bits of code. I don't think you can pin objects in memory for an extended period of time. The spec also mentions that these methods don't necessarily pin the object. You still might get a copy.
Offline zingbat

Senior Devvie




Java games rock!


« Reply #15 - Posted 2007-01-10 17:40:42 »

If you'd be willing to post your code, I'd be happy to look at it and point out any potential problems, but in the absence of that I have to warn people on these forums not to get too excited. I don't think there's any possible way this can be done safely given the current primitives in the JVM and Java libraries.

Would it make sense to use Buffers to backup vectors instead of a class Vec { float x; float y; float z} thing?

Something like a class VectorBuffer implemented on a Float Buffer.

We could even use generics for this: class VectorBuffer<class T> where T can be ByteBuffer, FloatBuffer, etc
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #16 - Posted 2007-01-10 18:46:17 »

I thought about it, like a sliding window, or struct.

It would make it a heck of a lot more safe (1 type per MemoryBlock)

Then you don't have to pass the offset in bytes (n * sizeof), but in structs (n)

I can't however make a {Type}Buffer at runtime, because you need it at compile-time to be usable.


StructBuffer struct = new StructBuffer(ByteBuffer bb, Vec.class);
Vec mapped1 = struct.get(n+13);
Vec mapped2 = struct.get(n+14);


I'm testing things now, it has serious advantages over the MemoryBlock class

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #17 - Posted 2007-01-10 20:44:00 »

Sent PM to Ken Russell...


Without a doubt he'll advice (more or less) against it, as it can't be 100.00% relied on Smiley

I think it's stable enough for games, I can't manage to crash it.



Current usage:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
      int instances = 2;
      StructBuffer<Vec> structBuffer = new StructBuffer(Vec.class, instances);

      Vec vec1 = structBuffer.get(0);
      Vec vec2a = structBuffer.get(1);
      Vec vec2b = structBuffer.get(1);

      // fill vec
      vec1.x = 4;
      vec1.y = 5;
      vec1.z = 6;
      System.out.println(vec1);

      // write 13 to buffer at the position of vec.y
      int sizeof = structBuffer.sizeof();
      int yOff = structBuffer.searchFieldOffset("y");
      structBuffer.backing().putInt((0 * sizeof) + yOff, 13);
      System.out.println(vec1);

      System.out.println("vec@2a == vec@2b: " + (vec2a == vec2b));


Output:
1  
2  
3  
Vec[x=4,y=5,z=6]
Vec[x=4,y=13,z=6]
vec@2a == vec@2b: true

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Ken Russell

JGO Coder




Java games rock!


« Reply #18 - Posted 2007-01-11 03:08:16 »

As Riven mentioned he sent me his source code (which, admittedly, is very clever). The basic trick is using sun.misc.Unsafe to fake up object references which point outside the garbage-collected heap. While this is a very clever trick, it doesn't work in the general case. The HotSpot garbage collector is a precise copying collector and at basically any time it may encounter one of these faked-up object instances and copy it into the heap, destroying the association between (in this case) the Vec and the backing Buffer. Actually, thinking more about how the mark-sweep collector works, I'm really not sure what will happen when it traverses one of these faked-up object instances. It may "just" leave the object header in a corrupted state, or possibly worse. Again, a GC and these nasty side-effects might happen at any time (basically during the execution of any Java bytecode), so this trick is completely unsuitable for use in any situation except as an interesting thought experiment.

You may want to look at the code GlueGen autogenerates on the Windows platform for JOGL. It creates Java classes which map to C structs. While the memory isn't overlaid (it uses New I/O internally), the mapping between fields of C structs and methods in the Java classes is very straightforward and easy to deal with. Techniques such as this are legal and are what I'd recommend as an alternative.
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #19 - Posted 2007-01-11 13:53:04 »

While respecting (and accepting) your advice, in the supplied code there was a stress-test which showed the (default?) GC doesn't copy the mapped objects's pointers / references to heap-memory. Could you explain this behaviour?



Could you please tell me whether Sun is working (or: going to work) on mapped-objects?

The only thing that would be required to make this work, is to make the GC *never* touch any object outside the heap, which seems to be quite easy to implement to me.

Thanks for your time.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #20 - Posted 2007-01-11 16:27:40 »

That'd depend on the GC algorithm being used. I believe the compacting GC will bugger it right up.

Perhaps you can pin the objects in JNI using getCritical thingies?

Cas Smiley

Could you tell me the vm-arguments to enable this compacting GC?

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline pepijnve

Junior Devvie




Java games rock!


« Reply #21 - Posted 2007-01-11 18:14:56 »

Could you tell me the vm-arguments to enable this compacting GC?

http://www-128.ibm.com/developerworks/java/library/j-jtp11253/
Lists the different options in a table about halfway the article
Offline bienator

Senior Devvie




OutOfCoffeeException


« Reply #22 - Posted 2007-01-11 18:30:37 »

Could you tell me the vm-arguments to enable this compacting GC?

I think there are three (or four) GCs in Java 6
1 -XX:-UseSerialGC (this would be the default one on desktops)
2 -XX:-UseConcMarkSweepGC (i think it's just for the young generation)
3 -XX:-UseParallelGC (young) -XX:-UseParallelOldGC (old and young generation)
4 -XX:-UseTrainGC (incremental one don't know if it still exists)

I' m not sure but I think 2 and 3 have a compacting part in there algorithm (I never read something about the first one), but If I where you i would try them all Wink
And don't forget -XX:+PrintGCDetails to trace if your App triggers full or just minor collections.

note: there are more options for the behavior of every collector, at least here are some of them: http://72.5.124.55/javase/technologies/hotspot/vmoptions.jsp

Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #23 - Posted 2007-01-11 19:31:35 »

I extended the testcase, to create hundreds of millions of objects, in hundreds of iterations, and got a crash after a few minutes

oh well, was fun while it lasted



Anyway, the question to Ken remains Smiley

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline zingbat

Senior Devvie




Java games rock!


« Reply #24 - Posted 2007-01-11 21:19:26 »

The only thing that would be required to make this work, is to make the GC *never* touch any object outside the heap, which seems to be quite easy to implement to me.

I guess that when the java vm becomes open-source there will be a tweaked java-vm project just to be used in java games.
Offline SluX

Junior Devvie





« Reply #25 - Posted 2007-01-11 22:57:06 »

I surely hope so...that is definitely needed if we want a more serious impact on industry...

Btw good job Riven.

"Intelligence is the most beautiful gift and the greatest temptation which one life can receive from the gods."Me Cheesy
Play strategic football
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #26 - Posted 2007-01-11 23:21:21 »

The crash is weird though, because at most the relation between the object and the buffer would be gone, if the GC did anything, it should never crash...

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #27 - Posted 2007-01-12 00:48:54 »

You may want to look at the code GlueGen autogenerates on the Windows platform for JOGL. It creates Java classes which map to C structs. While the memory isn't overlaid (it uses New I/O internally), the mapping between fields of C structs and methods in the Java classes is very straightforward and easy to deal with. Techniques such as this are legal and are what I'd recommend as an alternative.

Hey Ken, has any work been done on fully documenting GlueGen?  I've never had the time to look into it but it sounds like something I could really use, as I need to do a lot of JNI work in my day job.
A tutorial on using GlueGen would make a great article for Java.net.

Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 841
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #28 - Posted 2007-01-12 01:32:33 »

mapping *methods* to struct-values is dead-easy, just make a buffer backing it, and give the proper offsets. basicly a struct, without fields...

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Ken Russell

JGO Coder




Java games rock!


« Reply #29 - Posted 2007-01-12 02:10:30 »

While respecting (and accepting) your advice, in the supplied code there was a stress-test which showed the (default?) GC doesn't copy the mapped objects's pointers / references to heap-memory. Could you explain this behaviour?

Upon looking again at HotSpot's serial mark/sweep collector, I think your fake Object references probably won't get copied, since the relevant phases of the collector operate by iterating the address space of the old generation. However, your mapped objects break invariants in the collector and you would almost certainly see assertion failures if you ran with a debug HotSpot build. Also, if you ran with a different garbage collector such as the Concurrent Mark-Sweep (CMS) collector (-Xincgc) which performs card marks for more than just pointer stores in the heap, you would probably be back to crashing the JVM.

Could you please tell me whether Sun is working (or: going to work) on mapped-objects?

There are no plans to do so. Unlike the C language, the Java language and VM spec do not specify the layout of objects in memory, so attempting to overlay them to arbitrary memory regions is meaningless and non-portable. There are ways of using annotations or tools like GlueGen to get Java object views on to C heap memory in a portable way. See the evaluation of bug ID 4820062 for a concrete proposal on how this might be done with no changes to the language or the JVM.

The only thing that would be required to make this work, is to make the GC *never* touch any object outside the heap, which seems to be quite easy to implement to me.

Sorry, but it isn't that easy. What is the desired data layout in memory? Right now as you've noticed you have somewhat meaningless object headers in between your Vec data, which probably makes it difficult to just send this data down to the graphics card. Would these objects support normal Java method dispatch rules? Synchronization? Do they support Object fields, or just fields of primitive type?

If all you want is to treat a large direct FloatBuffer as a sequence of Vecs, you can mimic this with New I/O and pure Java APIs. I think some of the JavaOne talk slides linked from the JOGL home page describe how you might make a VecCollection class which returns Vecs which are basically just integer indices into a backing FloatBuffer and whose getX()/setX() methods just make calls on the backing FloatBuffer. This is similar to what you're doing but has the advantage of being portable and correct.
Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (33 views)
2014-12-15 09:26:44

Mr.CodeIt (23 views)
2014-12-14 19:50:38

BurntPizza (51 views)
2014-12-09 22:41:13

BurntPizza (84 views)
2014-12-08 04:46:31

JscottyBieshaar (45 views)
2014-12-05 12:39:02

SHC (59 views)
2014-12-03 16:27:13

CopyableCougar4 (59 views)
2014-11-29 21:32:03

toopeicgaming1999 (123 views)
2014-11-26 15:22:04

toopeicgaming1999 (114 views)
2014-11-26 15:20:36

toopeicgaming1999 (32 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!