logo logo

 Back to main page

The NWNX Community Forum

 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
 
nwnx_jvm - native JVM integration
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9  Next
 
Post new topic   Reply to topic    nwnx.org Forum Index -> Linux development
View previous topic :: View next topic  
Author Message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Fri Dec 04, 2015 18:48    Post subject: Reply with quote

Hi Elven
I was just wondering if there is supposed to be a performance gain when using java opposed to ncs scripts?


Quote:

Demand ResRef: resmantest.2da
Returning our own 2da table!
Got data from Hook, returning (size = 40): 2DA V2.0

A B
0 a1 b1
1 a2 b2
H
Tested Resman hook: a2
Demand ResRef: test_java.ncs
Took 18 ms to complete 100000 get name calls in ncs
100000 times getModuleName() took 206 ms: CEP - Starter 2.60



Did the following

Code:

void main()
{
   PrintString("Testing call from Java!");
   object oMod = GetModule();
   SetLocalInt(oMod,"TESTING_JAVA",1212);
   int i = 0;
   string name;
   for(i=0;i<=100000;i++)
   {
      name = GetModuleName();
   }
   PrintString(name+" Done!!");


}


with the following java code:

Code:

long startbench = System.currentTimeMillis();
                  String modName = "";
                  for (int i = 0; i < 100000; i++){
                     modName = NWScript.getModuleName();
                  }
                  long timebench = System.currentTimeMillis() - startbench;
                  NWScript.printString("100000 times getModuleName() took " + timebench + " ms: "+ modName);
                  String testResman = NWScript.get2DAString("resmantest", "A", 1);
                  if (!testResman.equals("a2"))
                     throw new RuntimeException("ResMan not working; expected 'a2', got '" + testResman + "'");
                  System.out.println("Tested Resman hook: " + testResman);
                  long startScriptTime = System.currentTimeMillis();
                  NWScript.executeScript("test_java", objSelf);
                  long timeDiff = System.currentTimeMillis() - startScriptTime;
                  System.out.println("Took "+timeDiff+" ms to complete 100000 get name calls in ncs");
                  System.out.println("100000 times getModuleName() took " + timebench + " ms: "+ modName);
                  int iTest = NWScript.getLocalInt(objSelf,"TESTING_JAVA");
                  if(iTest != 1212){
                     throw new RuntimeException("Java execute script call not working; expected '1212', got '" + iTest + "'");
                  }




I was kinda hoping that bypassing the ncs scripts and doing complex functionality in pure java, would be somehow more efficient.

This benchmark suggests that doing something in Java can be about 100x slower?

18ms for 100,000 calls to GetModuleName() in ncs
vs
206ms for 100,000 calls to the Java equivalent.

Is this expected?
Back to top
View user's profile Send private message
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Fri Dec 04, 2015 19:03    Post subject: Reply with quote

Baaleos wrote:
I was kinda hoping that bypassing the ncs scripts and doing complex functionality in pure java, would be somehow more efficient.

This benchmark suggests that doing something in Java can be about 100x slower?

18ms for 100,000 calls to GetModuleName() in ncs
vs
206ms for 100,000 calls to the Java equivalent.

Is this expected?


Sorry about not getting back to your previous post. Pretty busy otherwise with a few projects! But it does look very ambitious. I do wonder how you manage to keep your setting working at all what with gods running around! ;)

As to your actual question:

All NWScript calls from the JVM side need to pass through JNI into nwserver. This is always slower because it adds an extra transition and context switch, trashing the cache. The 100x times slowdown looks high, but may be true with your synthetic benchmark. I'm sure there's some space inside nwnx_jvm to optimise call performance.

Actual expected performance is a complex question and highly dependant on the applied performance tweaks by the JVM, so it may vary with runtime; but it will never be faster than NWScript itself.

If you absolutely need better performance you would need to bypass the NWScript VM altogether and access NWN memory directly. The easiest way to do this would be to make the actual nwnx2 api accessible via jni, but that also brings you into the territory where incorrect usage will crash your server.

But even then, I suspect it will never outmatch native performance in your given benchmark.

I don't think the performance drop is a huge issue in practice though (the NWN VM is still crazy fast on modern CPUs for what it does, and so is Java). The advantages the JVM gives you far outweigh the abstract cost.
_________________
silm.pw, a player-driven Forgotten Realms NWN1 persistent world for tinkerers.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Fri Dec 04, 2015 19:37    Post subject: Reply with quote

Yeah I mean the performance hit is not that scary.
I guess I always assumed that java being a proper compiled language would be faster than ncs which is more of a scripting language.

But I do understand the issue here:
Basically for every java -> NWScript call - there is a call to VirtualMachine
Where as something in NCS is already in the VirtualMachine etc
or something like that:


Yeah the God system was something that was dished out sparingly to players.
Eg: Either a long quest to unlock, or random event.
I also made it at first so players had access to it on a subscription basis.
Eg: Posts on the forum give credits which buy 1 month of god access.

I did away with that model eventually going for an ingame quest to unlock it.

There is always the potential that you get a Bad god who kills players and annoys people.

This is why I would typically add hooks onto all God interactions to make it so Gods cannot mess with players in the most malicious ways.

Eg: A player who chooses to be exempt from the Religion System- cannot be interacted with by a God.
Eg: The players cannot see them, The Gods cannot use divine abilities on them, and Spells from Gods cannot be cast on those players.

At the same time - it means that the player who does not want any god interfering with them, cannot be aided by the God.
I had a passive system also added to the server.


Each kill takes a portion of the xp from monster kills, and routes it to the God as energy gain. (Champions doing feats of strength for their god)
The more energy they siphon off for their god, gives them reputation with their god.

Even when their god is offline, the system would then randomly during combat aid the player, based on the amount of energy collected.
This could either be a buff/effect applied to the player, or an actual blast of energy from the heavens against one or more of their attackers etc.

The system had a long cooldown to balance it - but the idea was to give players the impression that their god was watching over them, and showed their favor by protecting the player.

As good as the God System was -
I actually loved the Dream and Curse system I added.

It was a dialogue which Gods could enter into, which allowed them to select players logged in, and

1. Write a short dream sequence.
This would be played to the player when they rest : basically a conversation pops up with the text that the god typed in.
Eg:

'You close your eyes and sense a feeling of nausea, you cannot feel the ground beneath your feet. Unable to tell whether you are falling or flying, you reach into the darkness hoping to grab hold of something to steady yourself. You sense someone or something is watching you, and as you turn you see a dark shadow with blazing eyes looking at you. You open your mouth to call out but the shadow turns into an icy vapor and flies towards you. You awaken in your bed, with a pain in your chest....
'What was that?'

So something like the above, would play back for the player on awakening from a dream.
Even better, is that the system would actually message the God who wrote the story/dream, that the dream has been received.
(Roleplay mechanic)

I remember a few times when I used to actually write these stories and wait around in anticipation for the players to rest and get my story.

The other system:

2. Curses
Gods have the ability to gift/curse players with a Feat called 'Elemental Exposure'
This feat was multipurpose - Active for spell casting abilities, but also passive damage mitigation/increase'

The system was once again done by dialogue, Gods can select from any logged in player, and curse the player with the element of their choice.
Opposing Gods cannot remove the curses, only the Gods who placed the Curse. (this however may or may not be desirable)

So examples of curses include:
Ice:
Player is immune to cold, but takes 50% extra fire.
Imagine Elsa from Frozen.

Abilities to shoot icy vapor blasts at targets - however an unskilled person in this feat will more often than not leave themselves drained and dazed.
Continued use of this skill will slowly increase proficiency in the skill, and reduce the chances of becoming dazed after use.

Passive skill makes it so the player when close to death can unleash a blast of elemental ice to the area around.
This is to symbolize them getting scared and panicking.
This has a random chance to occur when getting hit below certain % of health.

Anyway - the system, like most systems I built, was inspired by from many different sources.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Mon Dec 14, 2015 14:55    Post subject: Reply with quote

So I am trying to get the Java threading model to work.
So far I am having difficulty with:

So far it will run : NWScript.printString("This came from askModuleToDoWork");

But it wont run anything within the Runnable()
Is there something I am missing?
I am assigning this to the Module object, trying to get it to at least print something to the log, from another thread.
Code:

private static final NWObject module = new NWObject(0);
   
   public static void askModuleToDoWork(NWObject objMod){
      NWScript.printString("This came from askModuleToDoWork");
      Scheduler.assign(module, new Runnable() {
            public void run() {
                 NWScript.speakString("hi", 0);
               System.out.println("Testing on thread!!");
               NWScript.printString("This came from another threads");
            }
         });

   }
Back to top
View user's profile Send private message
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Mon Dec 14, 2015 15:01    Post subject: Reply with quote

Baaleos wrote:
So I am trying to get the Java threading model to work.
So far I am having difficulty with:

So far it will run : NWScript.printString("This came from askModuleToDoWork");

But it wont run anything within the Runnable()
Is there something I am missing?
I am assigning this to the Module object, trying to get it to at least print something to the log, from another thread.
Code:

private static final NWObject module = new NWObject(0);
   
   public static void askModuleToDoWork(NWObject objMod){
      NWScript.printString("This came from askModuleToDoWork");
      Scheduler.assign(module, new Runnable() {
            public void run() {
                 NWScript.speakString("hi", 0);
               System.out.println("Testing on thread!!");
               NWScript.printString("This came from another threads");
            }
         });

   }


Two things:

a) Scheduler is not threaded. It's just a synchronised queue.

b) You need to call Scheduler.flushQueues() after each event or the queue with assigns will never clea; best done just after your event() handler. This is something that should probably go into the library itself. Not sure why that isn't in.
_________________
silm.pw, a player-driven Forgotten Realms NWN1 persistent world for tinkerers.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Mon Dec 14, 2015 15:05    Post subject: Reply with quote

Although it is not threaded, will calling it from another thread allow it to interact with MainLoop?

Eg:
Can I do lots of complex complicated computations on thread 2 (a Java thread), and then send commands to MainLoop on thread 1 of nwserver - which will then be actioned via the Scheduler?

Eg:

Lets say I have a thread/Loop in Java that computes hunger.
It is on another thread, and it basically just slowly diminishes a number down to 0.

When it gets to 0 - can I call Scheduler.# to execute a NWScript function on an object?

PS- Thanks for the fast response- that was very speedy
Back to top
View user's profile Send private message
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Mon Dec 14, 2015 15:06    Post subject: Reply with quote

Baaleos wrote:
Although it is not threaded, will calling it from another thread allow it to interact with MainLoop?

Eg:
Can I do lots of complex complicated computations on thread 2 (a Java thread), and then send commands to MainLoop on thread 1 of nwserver - which will then be actioned via the Scheduler?

Eg:

Lets say I have a thread/Loop in Java that computes hunger.
It is on another thread, and it basically just slowly diminishes a number down to 0.

When it gets to 0 - can I call Scheduler.# to execute a NWScript function on an object?

PS- Thanks for the fast response- that was very speedy


Yes, it's synchronised, a.k.a. threadsafe in java-lingo. You still need to take care about your own data structures if you access them from scheduled assigns and other threads though.
_________________
silm.pw, a player-driven Forgotten Realms NWN1 persistent world for tinkerers.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Mon Dec 14, 2015 15:10    Post subject: Reply with quote

Yay
Thanks - that worked - Im in work right now, and cant be arsed doing work.
So I just interject and waste time by playing with nwnx_jvm Lol

Its now printing to the nwserverLog.txt - so I have an understanding of how to implement a multi threaded application, within nwserver.
Back to top
View user's profile Send private message
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Mon Dec 14, 2015 15:12    Post subject: Reply with quote

Baaleos wrote:
Yay
Thanks - that worked - Im in work right now, and cant be arsed doing work.
So I just interject and waste time by playing with nwnx_jvm Lol

Its now printing to the nwserverLog.txt - so I have an understanding of how to implement a multi threaded application, within nwserver.


Bad Baaleos, get back to work. :p

Your approach is right, but you shouldn't use manual thread instanciation in java though. If you haven't yet, look into ExecutorService and friends.
_________________
silm.pw, a player-driven Forgotten Realms NWN1 persistent world for tinkerers.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Mon Dec 14, 2015 15:32    Post subject: Reply with quote

Quote:

FATAL ERROR in native method: Detaching native NWN thread from JVM failed.
at org.nwnx.nwnx2.jvm.NWScript.executeScript(Native Method)
- locked <0x79c00558> (a java.lang.Class for org.nwnx.nwnx2.jvm.NWScript)
at org.nwnx.nwnx2.jvm.Scheduler.assignSet(Unknown Source)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
- locked <0x79c06518> (a java.util.Collections$SynchronizedMap)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
at org.baaleos.systems.god.GodEnergyCalculator.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)



Code:

public class GodEnergyCalculator implements Runnable  {
   public GodEnergyCalculator(){
      //module = mod;
   }
   
   public void run()
   {
      NWScript.printString("Starting While loop");
      int i = 0;
      while(true){
         i++;
         try {
            Thread.sleep(5000);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }

         Scheduler.assign(module, new Runnable() {
            @Override
               public void run() {
                  NWScript.printString("This came from another thread");
               }
            });
         Scheduler.flushQueues();
         if(i >= 100)
         {
            break;
         }
      }
      
   }
   
   
   private static final NWObject module = new NWObject(0);
   
   public static void askModuleToDoWork(NWObject objMod){
      GodEnergyCalculator gCalc = new GodEnergyCalculator();
      Thread t = new Thread(gCalc);
      t.start();
      
      
   }
   
}





It does print the 'This came from another thread' message once in the logs, but then it exceptions out.
I get that I am not doing threading in a particularly nice way - but shouldn't this work?
Back to top
View user's profile Send private message
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Mon Dec 14, 2015 15:35    Post subject: Reply with quote

Baaleos wrote:
Quote:

FATAL ERROR in native method: Detaching native NWN thread from JVM failed.
at org.nwnx.nwnx2.jvm.NWScript.executeScript(Native Method)
- locked <0x79c00558> (a java.lang.Class for org.nwnx.nwnx2.jvm.NWScript)
at org.nwnx.nwnx2.jvm.Scheduler.assignSet(Unknown Source)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
- locked <0x79c06518> (a java.util.Collections$SynchronizedMap)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
at org.baaleos.systems.god.GodEnergyCalculator.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)



It does print the 'This came from another thread' message once in the logs, but then it exceptions out.
I get that I am not doing threading in a particularly nice way - but shouldn't this work?


Looks like a bug. I'll check it out.
_________________
silm.pw, a player-driven Forgotten Realms NWN1 persistent world for tinkerers.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Fri Mar 18, 2016 15:51    Post subject: Reply with quote

Hi Elven,
Was wondering if the above bug was ever resolved or a solution found?

I have restarted my linux PW development and use of Java would be a huge + for me.

FATAL ERROR in native method: Detaching native NWN thread from JVM failed.
at org.nwnx.nwnx2.jvm.NWScript.executeScript(Native Method)
- locked <0xb9c00558> (a java.lang.Class for org.nwnx.nwnx2.jvm.NWScript)
at org.nwnx.nwnx2.jvm.Scheduler.assignSet(Unknown Source)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
- locked <0xb9c06aa0> (a java.util.Collections$SynchronizedMap)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
at org.baaleos.systems.god.GodEnergyCalculator.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Back to top
View user's profile Send private message
elven



Joined: 28 Jul 2006
Posts: 259
Location: Germany

PostPosted: Fri Mar 18, 2016 19:34    Post subject: Reply with quote

Baaleos wrote:
Hi Elven,
Was wondering if the above bug was ever resolved or a solution found?

I have restarted my linux PW development and use of Java would be a huge + for me.

FATAL ERROR in native method: Detaching native NWN thread from JVM failed.
at org.nwnx.nwnx2.jvm.NWScript.executeScript(Native Method)
- locked <0xb9c00558> (a java.lang.Class for org.nwnx.nwnx2.jvm.NWScript)
at org.nwnx.nwnx2.jvm.Scheduler.assignSet(Unknown Source)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
- locked <0xb9c06aa0> (a java.util.Collections$SynchronizedMap)
at org.nwnx.nwnx2.jvm.Scheduler.flushQueues(Unknown Source)
at org.baaleos.systems.god.GodEnergyCalculator.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)


I kind of got distracted with other things and never looked into this again. Sorry.

The whole plugin could do with some love - code is pretty cobbled-together - but I can't promise when I get around to it. I've left it open as a tab though to remind me.

Can you give me some bullet points on what you want to do with nwnx_jvm, so I can get an idea on what to expect you will need performancewise?
_________________
silm.pw, a player-driven Forgotten Realms NWN1 persistent world for tinkerers.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Wed Apr 06, 2016 14:03    Post subject: ideas Reply with quote

Hi Elven,
Sorry, only just replying.

So what I would like to do with nwnx_jvm is be able to execute tasks on background threads, which are non-blocking to the mainloop.
The idea would be that I could develop systems that area intensive in looping through many objects/entities, and executing various NWScript calls based on the conditions found therein.

Performance wise - I could live with slower than native NWScript, its really the background thread operation I am looking forward to.

The current project I am working on, is a refactoring of my Genetics system in nwn, to java.
Before - it was falling down because of recursion and TMI's in NWScript.
However, in order to solve that, I had to add delay command with staggering by 0.10 seconds per object.
Not ideal, because then the heartbeats end up running less than every 7 seconds.

So, what I am looking to try is refactor it to Java, and have the heartbeat run in Java.

Code:

public static void GeneticsLoop(){
      
      while(true){
         NWObject[] obj = NWScript.getPCs();
         int TimeOfDayCurrent = GetCurrentTime();
         for(NWObject PC: obj){
            ProcessPlayer(PC, TimeOfDayCurrent);
         }
         
         try {
            Thread.sleep(7000);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }
      }
   }



Eg: Running this method on a background thread.

Which then calls

Code:

public static void ProcessPlayer(NWObject player, int timeOfDayCurrent){
      ProcessGenome(player, timeOfDayCurrent);
   }


then calls

Code:

int AmountOfGenesOnCreature = NWScript.getLocalInt(player,"creature_gene_count_");
      int i = 1;
      NWObject oArea = NWScript.getArea(player);
      NWLocation l = NWScript.getLocation(player);
      int iIsInWater = IsInWater(l);
      int AreaLocation = NWScript.getIsAreaAboveGround(oArea)==true ? 1:0;
      int Interior = NWScript.getIsAreaInterior(oArea) ==true ? 1:0;
      int Natural = NWScript.getIsAreaNatural(oArea) ==true ? 1:0;
      for(i=1;i<=AmountOfGenesOnCreature;i++){
         int GeneID = NWScript.getLocalInt(player,"creature_genome_storage_"+i);
         if(GeneID != 0){
            Gene geneToApply = new Gene(GeneID);
            
            HeartbeatProcessGene(player,geneToApply, TimeOfDayCurrent,oArea,iIsInWater,AreaLocation,Interior,Natural);
         }
      }



Actually - just on this,
The iIsInWater method I have above.
It uses a nwnx_funcxext method - to get the tile surface.
I know you said before that calling other plugins currently didn't work.
I was just wondering if calling the plugins via the SetLocal's would work?
Eg:
I added in
Code:

static int GetSurface(NWLocation lLocation) {
       NWObject oArea = NWScript.getAreaFromLocation(lLocation);
       NWVector vPos = NWScript.getPositionFromLocation(lLocation);

       NWScript.setLocalString(oArea, "NWNX!FUNCSEXT!GETSURFACE", Float.toString(vPos.getX())+"?"+Float.toString(vPos.getY())+"?"+Float.toString(vPos.getZ()));
       String sRet = NWScript.getLocalString(oArea, "NWNX!FUNCSEXT!GETSURFACE");
       NWScript.deleteLocalString(oArea, "NWNX!FUNCSEXT!GETSURFACE");
       return Integer.valueOf(sRet);
   }


Just wondering if this would simulate a NWScript invocation of SetLocalString, which would then be mistaken by nwnx as a native call, and therefore route the execution to the external plugin?
Or is nwnx setup in such a way that it cannot handle a call to one plugin while processing another?


Anyway.
If you have any examples of how to implement a background thread, that can then execute make NWScript calls, be very grateful.


Note - the other thing I like about nwnx_jvm is that it lets us create real classes, opposed to silly little structs in nwscript.
As you can see, I create a 'Gene' class.
I will also be creating a Genome class which is a collection of Genes etc
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Wed Apr 06, 2016 14:35    Post subject: Reply with quote

Code:

public static void ProcessPlayer(NWObject player, int timeOfDayCurrent){
      Genome genome = new Genome(player);
      ProcessGenome(player, genome, timeOfDayCurrent);
   }
   
   public static void ProcessGenome(NWObject player, Genome genome, int TimeOfDayCurrent){
      
      int i = 1;
      NWObject oArea = NWScript.getArea(player);
      NWLocation l = NWScript.getLocation(player);
      int iIsInWater = IsInWater(l);
      int AreaLocation = NWScript.getIsAreaAboveGround(oArea)==true ? 1:0;
      int Interior = NWScript.getIsAreaInterior(oArea) ==true ? 1:0;
      int Natural = NWScript.getIsAreaNatural(oArea) ==true ? 1:0;
      for(Gene g : genome){
         HeartbeatProcessGene(player,g, TimeOfDayCurrent,oArea,iIsInWater,AreaLocation,Interior,Natural);
      }
   }



Changing my genetics system to use real classes, opposed to structs, allowed me to refactor my code to look like this.
I am able to get the collection of Genes just by doing
Code:
Genome myGenome = new Genome(player);
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    nwnx.org Forum Index -> Linux development All times are GMT + 2 Hours
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9  Next
Page 8 of 9

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group