Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (499)
Games in Android Showcase (118)
games submitted by our members
Games in WIP (568)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: 1 ... 3 4 [5] 6 7
  ignore  |  Print  
  Once again! fast MappedObjects implementation  (Read 31011 times)
0 Members and 2 Guests are viewing this topic.
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #120 - Posted 2011-07-11 17:54:30 »

Quote
http://indiespot.net/files/published/mappedobject-0.10.jar (view source)
Main-class: org.lwjgl.util.mapped.TestMappedObject
JARs asm-3.2.jar and asm-util-3.2.jar (asm.ow2.org) must be on the classpath.

Changes:
  • Added code that measures how long the transforming takes (please report it if transforming takes too long!)
  • Refactored the MappedObject.init(...) method out of the public API
  • Added basic support for 'view-connected' MappedObjects


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
   static void testMappedSet()
   {
      MappedVec2 vec2 = MappedVec2.malloc(3);
      MappedVec3 vec3 = MappedVec3.malloc(3);

      MappedSet2 set = MappedSet.create(vec2, vec3);

      assert (vec2.view == 0);
      assert (vec3.view == 0);

      set.view = 2;
      assert (vec2.view == 2);
      assert (vec3.view == 2);

      set.view = 0;
      assert (vec2.view == 0);
      assert (vec3.view == 0);
   }

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

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #121 - Posted 2011-07-11 22:02:16 »

v0.10 is now in lwjgl-util (nightlies) Cool

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline theagentd
« Reply #122 - Posted 2011-07-12 10:06:15 »

Version 0.10 broke something for me! The exact same code that worked with version 0.9 doesn't work anymore because of the LWJGL library being loaded twice.
1  
Exception in thread "main" java.lang.UnsatisfiedLinkError: Native Library C:\Users\Mokyu\lib\lwjgl-2.7.1\native\windows\lwjgl.dll already loaded in another classloader


My main() function:
1  
2  
3  
4  
5  
6  
7  
8  
public static void main(String[] args) {
    MappedObjectTransformer.register(MappedParticle.class);
    if (MappedObjectClassLoader.fork(ParticleTest7.class, args)) {
        return;
    }

    new ParticleTest7().gameloop();
}


Also, I found out that I was using the client VM for my previous tests. With the server VM I actually get 60 FPS with 1 million particles on a laptop! Insane!
EDIT: With MappedObject on the server VM, I get about 1.5x increase in raw particle performance (I fill the buffers each frame, but OpenGL isn't involved at all) compared to puts.

Myomyomyo.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #123 - Posted 2011-07-12 13:20:21 »

the class ParticleTest7 probably causes the native libraries of LWJGL to be loaded.

What if you move the main-method out of the class that does anything LWJGL related?

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Spasi
« Reply #124 - Posted 2011-07-12 13:37:13 »

The next nightly will have the following improvements:

- Additional documentation.
- Support for bounds checking. Enabled with -Dorg.lwjgl.util.mapped.Checks=true.
- Timing and activity debug output has to be enabled with system properties as well (org.lwjgl.util.mapped.PrintTiming and .PrintActivity). org.lwjgl.util.Debug needs to be true at the same time.
Offline Spasi
« Reply #125 - Posted 2011-07-12 13:47:13 »

I just noticed that mapping a buffer always uses the base buffer address as the starting point for the mapped object. This doesn't strictly follow the LWJGL model of always using the current .position() for whatever you're trying to do. Do you mind if I change it to work that way?
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #126 - Posted 2011-07-12 14:08:20 »

Sure.

Nice job on the javadoc.

Regarding the logging, IMHO
http://java-game-lib.svn.sourceforge.net/viewvc/java-game-lib/trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java?revision=3572&view=markup
line 65 is way too important to hide by default.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Spasi
« Reply #127 - Posted 2011-07-12 14:22:43 »

OK, added .position() to mapping and reverted to System.err for the client warning.
Offline theagentd
« Reply #128 - Posted 2011-07-12 15:29:26 »

the class ParticleTest7 probably causes the native libraries of LWJGL to be loaded.

What if you move the main-method out of the class that does anything LWJGL related?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public class ParticleTest7Launcher {

    public static void main(String[] args) {
        MappedObjectTransformer.register(MappedParticle.class);
        if (MappedObjectClassLoader.fork(ParticleTest7Launcher.class, args)) {
            return;
        }

        new ParticleTest7().gameloop();
    }
}

This doesn't change anything, still the same error. I suppose the problem is that a library load is triggered during the transformation before the fork. (Huh)
What am I doing wrong?! T___T

I used a slightly hacky reflection-thingy to check what libraries are loaded at different points in the program. The crash happens on my first use of Display in the constructor of ParticleTest7. However, just before I start creating the Display, the LWJGL library does NOT seem to be loaded already! My breakpoints in the loadLibrary(String) function also seem to point to that when Display is used, it tries to load the LWJGL library TWICE. I'm completely confused.... As I said, it works like a charm in v0.9................

Myomyomyo.
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #129 - Posted 2011-07-12 16:41:49 »

If nothing else, I might implement fork(...) in such a way, that it really spawns another JVM.

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

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #130 - Posted 2011-07-12 17:06:53 »

I can reproduce it, which is good news Smiley

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

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #131 - Posted 2011-07-12 20:08:12 »

Unfortunately it's not easy to solve. Rolling back to v0.9 is not an option as javac generates bytecodes that not quite transform correctly, it seems. The lib was developed in Eclipse, so I never saw the odd bytecodes javac generated.
I can tell ASM to make the required stack frame calculations, but that triggers the traversal of classes (including org.lwjgl.Sys) which triggers the first time the lwjgl natives are loaded. In the new classloader Sys is eventually loaded again causing the error-message you saw.

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

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #132 - Posted 2011-07-12 21:43:00 »

Quote
http://indiespot.net/files/published/mappedobject-0.11.jar (view source)
Sourcecode is a reformatted checkout from the LWJGL nightlies.
Main-class: org.lwjgl.util.mapped.TestMappedObject
JARs asm-3.2.jar and asm-util-3.2.jar (asm.ow2.org) must be on the classpath.

Changes (Spasi)
  • Optional bounds checking on view field.

Bugfix (Riven)
  • No more double loading libraries caused by the computation of stack frames resulting in spurious class initialization during transform.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline theagentd
« Reply #133 - Posted 2011-07-13 10:00:49 »

Works wonders! Thank you so much for the fix! Now to try out the MappedSet class... =D

Myomyomyo.
Offline R.D.

Senior Member


Medals: 2
Projects: 1


"For the last time, Hats ARE Awesome"


« Reply #134 - Posted 2011-07-13 12:05:57 »

Puh, I really really wan't to understand the whole mapped objects stuff, but I can't get into my brain (maybe because exams)... Let's say I have a bunch of entities and now I want to switch to mapped objects, how to I make this happening?

Anyway, really great stuff. Read through some stuff Spasi posted and looks like i was blinded bei OOP Cheesy
Offline gouessej
« Reply #135 - Posted 2011-07-13 15:15:50 »

Hi

Can I safely call the clear() method on an instance whose class is a subclass of MappedObject?

Offline Spasi
« Reply #136 - Posted 2011-07-13 15:42:18 »

If you mean that the subclass has a .clear() defined, then it's safe because MappedObject doesn't have a .clear(). If you mean you need a .clear() method in MappedObject, that would not be very useful, it's as simple as doing .view = 0.
Offline Spasi
« Reply #137 - Posted 2011-07-13 16:48:11 »

I created a benchmark to test the difference between mapped iteration and plain array iteration. You can download the test code here.

Basically, the idea is that you have a loop somewhere, you go through your mapped data and perform an action on each element. The loop is very simple and you don't pass the mapped object to another method, everything happens in the loop code. Although this case is quite simple and doesn't describe every scenario, it will also be very common.

The important point here is that you don't care about what happens to the current view offset. The current .view could have been anything before entering the loop and it will be something (mostly) useless after it (for the code after the loop). So, if we assume this is true, the problem is that every time you set the current view in the loop, you're basically changing a value in system memory. Whereas in the case of array iteration, you only change the value of a CPU register. This implies a performance overhead that can become quite big, depending on the complexity of the mapped data and the computation that's happening. For the simplest case I'm testing (a mapped object holding a single integer), the performance difference is a bit over 3x in favor of array access.

I guess there are better ways to solve this, but one simple solution would be introducing a second way to set the current view, that would only be valid in the current method/scope (.localView or .scopeView?). The user would only need to know/care that .view is "sticky" and .localView is temporary. So, both of these methods would have identical results:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
void testView(MappedFoo foo) {
   for ( int i = 0; i < 100; i++ ) {
      foo.view = i;
      // do something with foo
  }
}

void testLocal(MappedFoo foo) {
   for ( int i = 0; i < 100; i++ ) {
      foo.localView = i;
      // do something with foo
  }
}


except that after testView foo.view will be equal to 99, but after testLocal it will be whatever it was before. As for what happens under the hood, see methods testView2 and testLocal in the code I linked above. That is what the bytecode transformer should output. If you run the benchmark, you'll see that testLocal has almost identical performance to testJava.

Implementation-wise, there are some complications (e.g. what happens in the method stack, we need a stack slot for each mapped object, for the local address variable), but I think it's doable. Riven would know better of course. What do you think?

edit: I guess it's obvious, but the local viewAddress in testView2 is needed so that you can mix/match .view and .localView in the same method.
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #138 - Posted 2011-07-13 17:01:47 »

Going to investigate the options.

Simply allocating a local-var-entry for every MappedObject instance is impossible due to flow-control: you can create 1000 instances in a loop and access them all in wildly random patterns.

A 'simple' option would be to create a fast-path, if there is no malloc/map/dup/slice or any field/array-access in a method. So simple that we can be sure how many MappedObject instances we have...

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Spasi
« Reply #139 - Posted 2011-07-13 17:06:50 »

I'm pretty sure you only need 1 local if you create 1000 instances in a loop. Basically you need 1 for every MappedObject variable in the code. This:

1  
2  
3  
4  
for ( i = 0; i < 1000; i++ ) {
   MappedFoo foo = MappedFoo.malloc(...);
   // do something with foo
}


requires only 1 local. Whereas this:

1  
2  
3  
4  
5  
6  
for ( i = 0; i < 1000; i++ ) {
   MappedFoo foo1 = MappedFoo.malloc(...);
   ...
   MappedFoo foo2 = foo1.slice(); // or .dup();
  // do something with foo1 and foo2
}


requires 2.
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #140 - Posted 2011-07-13 17:11:37 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
MappedFoo[] foos = new MappedFoo[1000];
for(int i=0; i<1000; i++)
{
   foos[i] = MappedFoo.malloc(3);
}

for(int i=0; i<63; i+=3)
{
   for(int j=0; j<63; j+=(i%3+1))
   {
       foos[i].view = i+j;
       foos[j].view = j*2;
       foos[j+i+j].view = 13+i;
       float abc = foos[j+i+j].x;
       foos[i].x = foos[j%2].y;
       foos[j].y = foos[999].z / abc;
   }
}


this example would require 5 localvars

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

Senior Member


Medals: 2
Projects: 1


"For the last time, Hats ARE Awesome"


« Reply #141 - Posted 2011-07-13 19:27:36 »

I created a benchmark to test the difference between mapped iteration and plain array iteration. You can download the test code here.

I tested this one, gives me the best result on JAVA benchmark (is this correct?)

And how comes that Server VM is better then Client VM? is this a common behavoir?
Offline Spasi
« Reply #142 - Posted 2011-07-13 21:23:09 »

Yes, the java test should be the fastest, it's simple array access. The server VM is able to perform much more advanced optimizations than the client VM, so the performance difference you're seeing is expected. The only drawback is that server takes longer than client to apply the full range of JIT optimizations, so it's trading quick start-up time for peak performance. JDK7 has support for tiered compilation (disabled by default currently), which is a balance between server and client.

As for the benchmark itself, it turns out my assumption about the memory write being the cause of the slowdown was wrong. Memory writes are cached as well, so only when a read is requested will the actual system memory update happen (or when the cache line is evicted). So, even though it'll never be as fast as a CPU register, it's not 3x slower. It turns out that the benchmark was flawed, in that it didn't use the mapped object stride for the iteration, it used SIZEOF instead. Using a constant there results in a fully optimized loop, similar to the array one (I compared the JITed assembly with the debug JDK), whereas using stride is much more work for the CPU. Riven is currently examining a way to get rid of stride without sacrificing the functionality it provided.
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #143 - Posted 2011-07-13 21:59:50 »

Upcoming version allows for a localvariable to be used as a view-controller, using the @MappedView annotation:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
   public static void testLocalView(MappedFloat m, @MappedView int i)
   {
      for (i= 0; i< 5; i++)
      {
         m.value = (float) Math.random();
         m.value *= 2.0f;
      }
   
      for (i= 0; i< 5; i++)
      {
         System.out.println("[" + i + "] =>" + m.value);
      }
   }


As you can see, the view of the mapped object is directly controlled by modifying the variable 'i' (in this case).

The variable 'i' is actually not transformed, which means that 'i++' is as fast as usual. The new behaviour is implemented by transforming the field-access to a pointer calculated with the value 'i' instead of 'mapped.view'.

The localView code is ~3.2x as fast, according to Spasi's new benchmarks (a bit faster than float[] access).
The mapped.view code is ~1.5x as fast, according to Spasi's new benchmarks (half the speed of float[] access).

Please keep in mind that I am still working on further optimisations and that Spasi's benchmarks are naturally not representing expected performance for the general case (there is no general case).

v0.10 (which we will now deem slow-ish) was already a huge performance boost, in real-world situations.


Barely tested v0.12 (beware of crashes)

Thanks to Spasi for benchmarking and analyzing the generated bytecode for bottlenecks.

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

Senior Member


Medals: 2
Projects: 1


"For the last time, Hats ARE Awesome"


« Reply #144 - Posted 2011-07-14 09:19:36 »

3.2 ? Awesome Cheesy Still don't get how this all works, I will ask some question in the future to finally understand it :A looks like something I will use for a bullet hell shooter test.
Offline theagentd
« Reply #145 - Posted 2011-07-14 13:48:23 »

Upcoming version allows for a localvariable to be used as a view-controller, using the @MappedView annotation:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
   public static void testLocalView(MappedFloat m, @MappedView int i)
   {
      for (i= 0; i< 5; i++)
      {
         m.value = (float) Math.random();
         m.value *= 2.0f;
      }
   
      for (i= 0; i< 5; i++)
      {
         System.out.println("[" + i + "] =>" + m.value);
      }
   }


As you can see, the view of the mapped object is directly controlled by modifying the variable 'i' (in this case).

The variable 'i' is actually not transformed, which means that 'i++' is as fast as usual. The new behaviour is implemented by transforming the field-access to a pointer calculated with the value 'i' instead of 'mapped.view'.
Does this mean that I can access the same MappedObject using local pointers from multiple threads without having to synchronize?

Myomyomyo.
Offline princec

JGO Kernel


Medals: 392
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #146 - Posted 2011-07-14 15:39:33 »

Hm, I'm sorry but this code is pretty unreadable. There's no connect between what is happening and what is written. It's going to lead to some really nasty obscure looking code.

Cas Smiley

Offline Spasi
« Reply #147 - Posted 2011-07-14 16:10:38 »

Does this mean that I can access the same MappedObject using local pointers from multiple threads without having to synchronize?

Synchronization is still necessary if different threads access the same part of mapped memory. But it does mean that you don't have to use .dup() to pass a different instance to each thread.

@princec: Normal .view access is still there and is much faster now, so you don't have to use this method if you think it's error prone. It can't be used in every scenario anyway.
Online Riven
« League of Dukes »

JGO Overlord


Medals: 803
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #148 - Posted 2011-07-14 17:01:06 »

Hm, I'm sorry but this code is pretty unreadable. There's no connect between what is happening and what is written. It's going to lead to some really nasty obscure looking code.

Fair point. The performance gain however is so big that we just have to keep it in the library.

I think it's fair to rename @MappedView to @DiehardMappedView (or something along those lines) to give a strong indication that average joe and even seasoned jane shouldn't use it.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline theagentd
« Reply #149 - Posted 2011-07-15 13:34:01 »

Aaaaw... I hate seasoning... Just wasted some money on some bread drenched in pepper. Japanese people are weird.
Uh, to be serious: I think performance is really important, but I totally agree with Princec. I definitely think you should have to link the i variable to the MappedObject you want to control...

Myomyomyo.
Pages: 1 ... 3 4 [5] 6 7
  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.

Riven (10 views)
2014-10-02 14:36:20

Pippogeek (41 views)
2014-09-24 16:13:29

Pippogeek (32 views)
2014-09-24 16:12:22

Pippogeek (22 views)
2014-09-24 16:12:06

Grunnt (48 views)
2014-09-23 14:38:19

radar3301 (30 views)
2014-09-21 23:33:17

BurntPizza (65 views)
2014-09-21 02:42:18

BurntPizza (37 views)
2014-09-21 01:30:30

moogie (44 views)
2014-09-21 00:26:15

UprightPath (53 views)
2014-09-20 20:14:06
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

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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!