Performance Characteristics of Papyrus Functions and Operati

Post » Thu Jun 21, 2012 11:45 pm

I'm also posting here in the CK thread because even though I made a mod, this is really about holding a discussion about the best way to use the Papyrus scripting language. I finally have my tester script neat enough for public viewing, so I've uploaded a little mod on NexusMods I'm calling http://skyrim.nexusmods.com/mods/18825. The ESP provided simply runs the scripts and is not a normal gameplay mod so this thread is as close as I'm going to come to giving a release notice for it.

I've you're a modder wondering which Papyrus script functions and operations are faster then you now have a way to find out. I've written an initial set of tests and if you're a script writer it should be easy enough for you to add your own to the list.

So what are my results so far? The raw data is here:
Spoiler
Papyrus Speed TesterObjectReference script testsOverhead Time:  0.013600     used by the timing function itselfBaseline Time:  0.013199     an 'empty' loop 1000 timesTest case 8:    12.858016     f = GetDistance(PlayerRef)Test case 7:    12.673988     f = self.GetDistance(PlayerRef)Test case 6:    12.948014     f = PlayerRef.GetDistance(self)Test case 5:    12.888017     b = IsLocked()Test case 4:    12.753998     f = GetPositionX()Test case 3:    12.745988     f = self.GetPositionX()Test case 2:    13.131989     f = XTest case 1:    13.151978     f = self.XReferenceAlias script testsOverhead Time:  0.013100     time used by the timing function itselfBaseline Time:  0.013400     'Empty' Loop 1000 timesTest case 45:   12.648018     Debug.GetVersionNumber()Test case 44:   12.712014     SKSE.GetVersionRelease()Test case 43:   13.050011     if ref.GetBaseObject() as Potion  |  endifTest case 42:   12.551986     if ref.GetType() == 46  |  endifTest case 41:   0.012098     if GameDaysPassed as GlobalVariable  |  endifTest case 40:   12.718003     if GameDaysPassed.GetType() == 9  |  endifTest case 39:   0.009546     s = "sample"Test case 38:   0.029388     i = GameDaysPassed.Value as intTest case 37:   0.027518     f = GameDaysPassed.ValueTest case 36:   0.028434     i = GameDaysPassed.GetValueInt()Test case 35:   0.017072     f = GameDaysPassed.GetValue()Test case 34:   12.950012     f = Utility.GetCurrentGameTime()Test case 33:   12.739990     f = PlayerRef.GetPositionX()Test case 32:   12.797974     f = ref.GetPositionX()Test case 31:   12.589997     f = PlayerRef.GetPositionX()Test case 30:   13.029968     f = PlayerRef.XTest case 29:   13.028077     f = ref.XTest case 28:   12.406006     f = PlayerRef.GetDistance(ref)Test case 27:   12.921876     ref = Game.GetPlayer()Test case 26:   0.031696     ref = GetRef()Test case 25:   0.021184     ref = GetReference()Test case 24:   0.009194     ref = PlayerRefTest case 23:   0.022252     ref = Game.GetForm(0x14) as ObjectReferenceTest case 22:   0.009192     ref = otherTest case 21:   0.012604     myActor = other as ActorTest case 20:   0.010586     ref = NoneTest case 19:   0.012588     i = 1  |  if true  |  i = 2  |  endifTest case 18:   0.008744     i = 1 + (true as int)Test case 17:   0.015898     i = Math.Floor(0.04)Test case 16:   0.008842     i = 0.04 as intTest case 15:   0.014084     if b | i=1 | endif | if !b | i=2 | endifTest case 14:   0.009972     if !b | i=1 | else | i=2 | endifTest case 13:   0.014116     if b | i=1 | elseif !b | i=2 | endifTest case 12:   0.012662     if b | b=false | elseif !b | b=true | endifTest case 11:   0.012398     if b==true | b=false | else | b=true | endifTest case 10:   0.010344     b = !bTest case 9:    0.008732     int n = 5Test case 8:    0.008734     i = 5Test case 7:    0.010072     if !i | i = 5 | endifTest case 6:    0.011458     if i > 0  |  endifTest case 5:    0.010316     if i == 0  |  endifTest case 4:    0.010160     if !i  |  endifTest case 3:    0.010128     if !b  |  b=true  |  endifTest case 2:    0.008736     b = trueTest case 1:    0.007236     0Testing Complete

A few of the highlights are:
  • The fastest operations are simple assignments like "n = 5" or "ok = true".
  • Checking to see if n != 5 before setting it to 5 is just wasteful since the if n != 5 part takes longer than just setting the variable.
  • Casting with (0.04 as int) is about twice as fast as Math.Floor(0.04).
  • GameDaysPassed.GetValue() is the best way to access a global variable. (But if you need to use it multiple times in a row, assign it to a local variable!)
  • Accessing a PlayerRef property is about 1000 times faster than calling Game.GetPlayer().
  • Accessing a property or variable is also about twice as fast as using GetReference().
  • Using ref.GetPositionX() is slighly faster than ref.X, but the difference is small because both are pretty slow.
  • All external script functions like Game.GetPlayer(), ref.GetPositionX(), ref.GetDistance(PlayerRef), etc. are approximately the same slow speed.
  • SKSE provided functions aren't any slower than similar game functions.
I suspect that the speed problem for the slower functions is that control is being passed to another script when they are called.

Please try the scripts, add your own cases and post the results so we can find out more about what is and isn't good practice.

Let the testing begin.
User avatar
Angela
 
Posts: 3492
Joined: Mon Mar 05, 2007 8:33 am

Post » Thu Jun 21, 2012 10:54 am

A great idea. One thing that could help this project though is to include this thread link in the readme, to make it easier for testers to add their results here.
User avatar
Carlos Rojas
 
Posts: 3391
Joined: Thu Aug 16, 2007 11:19 am

Post » Thu Jun 21, 2012 4:25 pm

Cool, I look forward to taking a good look at this when I have internet access that's not just on my phone.

In the meantime, I just thought I'd provide a like to http://www.gamesas.com/topic/1381253-what-is-the-disadvantage-to-importing-functions-into-a-script/page__pid__20964937, as it contains a lot of good and relevant information.

Cipscis
User avatar
Andres Lechuga
 
Posts: 3406
Joined: Sun Aug 12, 2007 8:47 pm

Post » Fri Jun 22, 2012 2:40 am

Awesome thread! I'm glad to see someone else being proactive as to pushing Papyrus to its limits... which means finding what those limits and shortcomings are! AND educating others about and discussing it! During my extensive testing and use of Papyrus, I corroborate your findings - though not with the hard, presentable data you have.. only anecdotal.

I agree with most everything you said... except for MAAAAYBE the part about SKSE. Somehow I just think SOME of those functions would take a little while, do to their complexity, not because it's 3rd party or whatever. I actually avoid SKSE like the plague since it's so early in the development (they just got the scripting to implemented not so long ago). I DID do a little work for another team's mod, which required my using SKSE, but that's the extent of my actually using/testing it.. so ultimately I yield to your presumably more extensive experience with it.

But for my Gokstad ship mod, I did all I could (and still am) to get it working on machines that are less-than DeepBlue-ish. That's when I discovered the thing about certain types of errors and how badly they affect performance. http://www.gamesas.com/topic/1370110-impact-of-system-stress-scripting-errors-on-game-performance/

During my travails, I wound up putting debug.notifs in my script to see just how fast and often certain things can (and actually do) run... and what effects them. I found that I could get a script to fire a while loop at close to .01sec... faster than anything anyone might need for Skyrim, I'm sure. But I quickly found that adding certian things into that loop definitely slow it down - which you've documented above. In case anyone was wondering, I had a counter variable tick off iterations... then every X firing it would show the notif. EG: if the script was set up with an onUpdate at .033, and the counter set to notif every 30th firing; it SHOULD notif once per second (obviously visible to the naked eye, without messing with logging and tracing).

But to emphasize a few things you say: I started assigning Player to a local ref in my onActiv instead of doing game.getPlayer() every blink-o-the-eye... as well as any actor I script intensely. I do this with certain math functions as well; like square rooting and sine-ing for instance. I DID take to using ref.X in lieu of ref.getPosX as it saves typing, and I saw no extreme slowdown... nice to know I was correct in that assumption. But setting actors and certain math functions to local refs really helps, especially when those results are used on multiple lines later... and this should be shouted from the rooftops!

Something I noticed you mention that I MAY diverge from (though I'm not sure, as the above kinda breezed over it): I have a certain block of code that polls my custom globals and stores them in local variables for later manipulation. An example is a 'speed' variable.. one block does the getValue saving directly to a local; then during more than a dozen calculations using that variable, I believe it works faster than doing the getValue each time (wash, rinse, repeat at about 1/.06sec or so). I'd be curious to know for sure if this is overkill or not - if anything, it saves typing getValue a bunch heheheh.

One last thing and I'll stop rambling... something that REALLY helps to speed up complex scripting - breaking up the code into seperate scripts (and even attaching to different objects and/or quests). This takes advantage of multi-threading instead of becoming a victim of it. The unfortunate thing I found, and it has caused me NOT to implement it in my mod yet, was that it runs TOO fast... I mean REEEALLY fast. I have to redo the entire timing, and tweak this, blah blah that... eventually this is where I'm heading though.

What I do is have one script with calculations, which are stored to globals (I haven't messed with directly accessing other scripts' variable yet, but I foresee that being relatively slow... something that needs testing). Another does the actual movements (dozens of lines) based on those calculations (also dozens of lines long). I also place 'special' operations in a third script (such as polling Player's movement without using SKSE).. which would take up a bunch more space/time if wrapped all in one.

Anyways - thanks for the thread and info!
User avatar
Ladymorphine
 
Posts: 3441
Joined: Wed Nov 08, 2006 2:22 pm

Post » Thu Jun 21, 2012 1:58 pm

[...]the part about SKSE. Somehow I just think SOME of those functions would take a little while, do to their complexity, not because it's 3rd party or whatever.

[...]I have a certain block of code that polls my custom globals and stores them in local variables for later manipulation. An example is a 'speed' variable.. one block does the getValue saving directly to a local; then during more than a dozen calculations using that variable, I believe it works faster than doing the getValue each time (wash, rinse, repeat at about 1/.06sec or so). I'd be curious to know for sure if this is overkill or not - if anything, it saves typing getValue a bunch heheheh.

One last thing and I'll stop rambling... something that REALLY helps to speed up complex scripting - breaking up the code into seperate scripts (and even attaching to different objects and/or quests). This takes advantage of multi-threading instead of becoming a victim of it. The unfortunate thing I found, and it has caused me NOT to implement it in my mod yet, was that it runs TOO fast... I mean REEEALLY fast. I have to redo the entire timing, and tweak this, blah blah that... eventually this is where I'm heading though.

You may be right that some specific SKSE functions will be slower than others, but I doubt we'll be able to really see it. The overhead of calling external functions (which includes most of the native functions in the game too) is so large that the extra time spent by SKSE will be insignificant. My point was that SKSE functions aren't inherently any slower just because they are implemented by SKSE.

If you're going to use a global variable for repeated calculations then it will definitely be faster to use "mySpeed = theGlobalSpeed.GetValue()" and then calculate based on the local mySpeed variable. In fact, using a temporary local variable for any external value will make things faster as long as you are going to be using that value multiple times in a row without needing to check for changes right away. On the other hand if you're just looking at the value once or twice, then waiting a while and checking again the temporary variable is a waste.

Be warned that if everyone decides to speed up their scripts by splitting them into 12 pieces the whole scripting system will become overloaded and slow. (Some people have had the bright idea to try and use more than one of the WiFi channels at a time to double, triple, or raise their performance even higher. What they forgot is that the multiple WiFi channels exist so that you can have multiple, independent WiFi networks near each other. If one person claims 5 channels, that means 4 other people will be shut out.)
User avatar
NAkeshIa BENNETT
 
Posts: 3519
Joined: Fri Jun 16, 2006 12:23 pm

Post » Thu Jun 21, 2012 7:23 pm

I have just decompiled three variations of a script and can definitively say that all three of these produce identical runnable code. (The time a script is compiled and the line number of each command get embedded in the compiled version of the script for debugging so the actual PEX files are different, but the runnable code parts are identical.)

Event OnInit()	Debug.Trace("Hi")EndEvent
import DebugEvent OnInit()	Debug.Trace("Hi")EndEvent
import DebugEvent OnInit()	Trace("Hi")EndEvent

The world "import" in the case just means making their names available it doesn't mean pulling in all of their code. So go ahead, import and save yourself some typing. Or don't import and make it obvious to yourself which functions come from which script type. Either way works and it's just a matter of your preferred style.
User avatar
Anna Watts
 
Posts: 3476
Joined: Sat Jun 17, 2006 8:31 pm

Post » Fri Jun 22, 2012 3:02 am

Question: What's the performance impact of Utility.RandomInt(1, 100), for example? RandomFloat? How much of an impact would calling it 10 times during a run-once script, but possibly as many as 10 or more times simultaneously (on an NPC spawning)?

Should I pre-prep variables or use less randomization?
User avatar
Ymani Hood
 
Posts: 3514
Joined: Fri Oct 26, 2007 3:22 am

Post » Fri Jun 22, 2012 12:31 am

I'm afraid the news is bad for random numbers. They are in the slow category.

Test case 12:   12.675995     if RandomInt(1, 100) > 50 | endifTest case 11:   12.743996     if RandomFloat(1, 100) > 50.0 | endifTest case 10:   12.630005     i = RandomInt(1,100)Test case 9:    12.531999     f = RandomFloat(1, 100)
So prep values where possible and make do with as few as you can.

If you're wanting something to happen quickly, you might store 10 pre-generated values for use then after you've made something happen that uses them let the script refill them. That way the player won't notice that filling them takes time. It does mean you'll need extra variables and you'll need to fill them the first time in your OnInit event.
User avatar
REVLUTIN
 
Posts: 3498
Joined: Tue Dec 26, 2006 8:44 pm

Post » Thu Jun 21, 2012 11:02 am

Thanks for the report. :)

I can prep values inside SkyProc before sending them as script properties to an NPC, it's just a matter of how I construct it all. It was a bit easier with the current framework to use some random number gen inside papyrus.. but I'll let java handle some numbers.
User avatar
luke trodden
 
Posts: 3445
Joined: Sun Jun 24, 2007 12:48 am

Post » Thu Jun 21, 2012 3:04 pm

I have another request (yes I'm shameless and should be doing this myself instead). Can you test the differences between getting a script's property, variable, and local variables? Example:

;some scriptInt Property IntProp AutoInt IntVarFunction SomeFunction(Int LocalInt)    ;which of the following 3 is fastest?    IntProp    IntVar    LocalIntEndFunction

I'm assuming that getting a script variable vs a local variable is going to be about the same, and that both are going to be faster than getting a property.
User avatar
Jennifer May
 
Posts: 3376
Joined: Thu Aug 16, 2007 3:51 pm

Post » Thu Jun 21, 2012 4:43 pm

iVar, iVarProp, OtherScript.iVarProp, and iVarGLOB took roughly the same amount of time to iterate 1,000 times.
Spoiler
  • Int iVar
    	While aiIteration < 1000		aiIteration += 1		If iVar == 1		EndIf	EndWhile[06/16/2012 - 11:02:50PM] Time elapsed 01: 0.014000[06/16/2012 - 11:02:57PM] Time elapsed 01: 0.017998[06/16/2012 - 11:03:03PM] Time elapsed 01: 0.017006[06/16/2012 - 11:03:12PM] Time elapsed 01: 0.014000[06/16/2012 - 11:03:19PM] Time elapsed 01: 0.015007
  • iVarGLOB
    	While aiIteration < 1000		aiIteration += 1		If iVarGLOB.GetValue() == 1		EndIf	EndWhile[06/16/2012 - 11:03:27PM] Time elapsed 02: 0.014999[06/16/2012 - 11:03:33PM] Time elapsed 02: 0.014000[06/16/2012 - 11:03:40PM] Time elapsed 02: 0.014999[06/16/2012 - 11:03:45PM] Time elapsed 02: 0.014000[06/16/2012 - 11:03:54PM] Time elapsed 02: 0.014999
  • iVarProp
    	While aiIteration < 1000		aiIteration += 1		If iVarProp == 1		EndIf	EndWhile[06/16/2012 - 11:04:03PM] Time elapsed 03: 0.016998[06/16/2012 - 11:04:10PM] Time elapsed 03: 0.015991[06/16/2012 - 11:04:17PM] Time elapsed 03: 0.014999[06/16/2012 - 11:04:25PM] Time elapsed 03: 0.013000[06/16/2012 - 11:04:32PM] Time elapsed 03: 0.014008
  • OtherScript.iVarProp
    	While aiIteration < 1000		aiIteration += 1		If OtherScript.iVarProp == 1		EndIf	EndWhile[[06/16/2012 - 11:04:40PM] Time elapsed 04: 0.014999[06/16/2012 - 11:04:46PM] Time elapsed 04: 0.014008[06/16/2012 - 11:04:53PM] Time elapsed 04: 0.016998[06/16/2012 - 11:05:05PM] Time elapsed 04: 0.016006[06/16/2012 - 11:05:13PM] Time elapsed 04: 0.014008
Globals, technically, 'win' by a nose. Incidentally, GLOB.Value, GLOB.GetValueInt(), and GLOB.GetValue() are all 'priced' the same.

Edit: Results were different when testing with 1,000,000 iterations. See below.
User avatar
Bellismydesi
 
Posts: 3360
Joined: Sun Jun 18, 2006 7:25 am

Post » Thu Jun 21, 2012 1:23 pm

Globals, technically, 'win' by a nose. Incidentally, GLOB.Value, GLOB.GetValueInt(), and GLOB.GetValue() are all 'priced' the same.

That seems to contradict tests 35 to 38 in the first post. I guess I'm just going to have to test it for myself.
User avatar
Phillip Hamilton
 
Posts: 3457
Joined: Wed Oct 10, 2007 3:07 pm

Post » Fri Jun 22, 2012 2:38 am

I think JustinOther is right on variables. The differences between properties, script variables, and local variables are so small that we can't measure them it they exist.

Accessing globals is an interesting case though. I checked repeatedly and GetValue is always a little faster than the other three ways of accessing a global.

(GetValue() as Int) and (Value as Int) will both be slower than GetValueInt() because the "as Int" part adds a very small amount of time.

And accessing globals is absolutely slower than accessing local variables but not by very much. All of the variable access methods are so fast that you need to run the testing loop 10000 or even 100000 times to see the differences. That's why JustinOther isn't seeing them.

So the good news is that unless you are creating a loop that will run hundreds or thousands of times you probably don't care about the differences.
User avatar
Dewayne Quattlebaum
 
Posts: 3529
Joined: Thu Aug 30, 2007 12:29 pm

Post » Thu Jun 21, 2012 3:37 pm

I bumped up the iteration count to 10^6 and...
Spoiler
  • Int iVar
    	While aiIteration < 1000000		aiIteration += 1		If iVar == 1		EndIf	EndWhile[06/17/2012 - 01:09:06PM] Time elapsed 01: 5.529999[06/17/2012 - 01:09:17PM] Time elapsed 01: 5.559006[06/17/2012 - 01:09:28PM] Time elapsed 01: 5.613998[06/17/2012 - 01:09:38PM] Time elapsed 01: 5.543007[06/17/2012 - 01:09:48PM] Time elapsed 01: 5.528999
  • iVarGLOB.GetValue()
    	While aiIteration < 1000000		aiIteration += 1		If iVarGLOB.GetValue() == 1		EndIf	EndWhile[06/17/2012 - 01:10:05PM] Time elapsed 02: 11.372993[06/17/2012 - 01:10:18PM] Time elapsed 02: 11.417007[06/17/2012 - 01:10:31PM] Time elapsed 02: 11.388000[06/17/2012 - 01:10:44PM] Time elapsed 02: 11.376999[06/17/2012 - 01:10:56PM] Time elapsed 02: 11.388000
  • iVarGLOB.Value
    	While aiIteration < 1000000		aiIteration += 1		If iVarGLOB.Value =http://forums.bethsoft.com/topic/1383987-performance-characteristics-of-papyrus-functions-and-operations/= 1		EndIf	EndWhile[06/17/2012 - 01:11:14PM] Time elapsed 03: 15.227005[06/17/2012 - 01:11:29PM] Time elapsed 03: 15.227005[06/17/2012 - 01:11:46PM] Time elapsed 03: 15.242004[06/17/2012 - 01:12:03PM] Time elapsed 03: 15.225006[06/17/2012 - 01:12:21PM] Time elapsed 03: 15.239990
  • iVarGLOB.GetValueInt()
    	While aiIteration < 1000000		aiIteration += 1		If iVarGLOB.GetValueInt() == 1		EndIf	EndWhile[06/17/2012 - 01:15:37PM] Time elapsed 04: 14.910000[06/17/2012 - 01:15:53PM] Time elapsed 04: 14.888000[06/17/2012 - 01:16:09PM] Time elapsed 04: 14.832005[06/17/2012 - 01:16:26PM] Time elapsed 04: 14.901001[06/17/2012 - 01:16:46PM] Time elapsed 04: 14.841995
  • iVarProp
    	While aiIteration < 1000000		aiIteration += 1		If iVarProp == 1		EndIf	EndWhile[06/17/2012 - 01:55:14PM] Time elapsed 01: 5.410004[06/17/2012 - 01:55:25PM] Time elapsed 01: 5.411003[06/17/2012 - 01:55:33PM] Time elapsed 01: 5.396996[06/17/2012 - 01:55:45PM] Time elapsed 01: 5.510002[06/17/2012 - 01:55:54PM] Time elapsed 01: 5.381996
  • OtherScript.iVarProp
    	While aiIteration < 1000000		aiIteration += 1		If OtherScript.iVarProp == 1		EndIf	EndWhile[06/17/2012 - 01:56:10PM] Time elapsed 02: 10.223999[06/17/2012 - 01:56:24PM] Time elapsed 02: 10.241989[06/17/2012 - 01:56:37PM] Time elapsed 02: 10.211990[06/17/2012 - 01:56:52PM] Time elapsed 02: 10.225998[06/17/2012 - 01:57:04PM] Time elapsed 02: 10.197998
...iVar was definitely faster, but GetValue() beat Value as did GetValueInt() but by a lesser margin.

Edit: Odd that GLOB.GetValue() 'won' the 10^3 round, yet took twice as long as iVar in the above test. 10^6 iterations would seem to provide more noticeable results.
Edit2: Added local/remote property tests above.
User avatar
Bellismydesi
 
Posts: 3360
Joined: Sun Jun 18, 2006 7:25 am

Post » Thu Jun 21, 2012 4:02 pm

Those results make sense. A local variable should be fastest. GetValue is just copying the value of the global without any special processing. GetValueInt is internally applying the cast from float to Int. (Yes, all globals are really stored as floating point values.) And access through Value has the (small) overhead of the property function access. But in practice they really aren't very different.

To keep things sane, my tester script tries 1000 iterations, then if it finds the instruction completed quickly it increases the number of iterations until it can either find a difference or it's obvious the difference is small enough that it doesn't matter. It's not quite as good as always testing 10^6 iterations, but I didn't think anyone (especially me) would be patient enough to wait over 4 hours just to know that Game.GetPlayer() is slow.
User avatar
[Bounty][Ben]
 
Posts: 3352
Joined: Mon Jul 30, 2007 2:11 pm

Post » Thu Jun 21, 2012 4:29 pm

Just a note on properties, SmkViper has mentioned that local auto properties have special optimisations:
Game.GetPlayer() is slower then accessing a variable (whether in the object or in a function) because it’s a function call.

A property will depend on a few things. If the property is an auto property and you’re calling it from the same script you defined it in then the compiler optimizes it and makes it just as fast as a variable. If the property isn’t an auto property, or you are accessing the property from a different script, then a function call is involved and the speed will depend on how “busy” the script that contains the property is (because the caller may have to wait).

Also, in the example posted by Rocket, playerRef will not have any value unless you give it one in the CK, or assign it a value somewhere else (like in your OnInit event).
Cipscis

EDIT:

Just want to check, is this the method you used to decompile the scripts?
PapyrusAssembler scriptname -D

Cipscis
User avatar
Leanne Molloy
 
Posts: 3342
Joined: Sat Sep 02, 2006 1:09 am

Post » Thu Jun 21, 2012 3:47 pm

I was under the assumption we can't decompile scripts... that they removed that option in the publicly released version of the apps. I just tried it as you typed it, and it comes up that it "cannot open store for class "test.pex", missing file?". I tried placing the PEX in the scripts folder, as well as the 'Papyrus Compiler' folder, same thing both ways. Maybe there's some other folder I should be trying?
User avatar
Marquis T
 
Posts: 3425
Joined: Fri Aug 31, 2007 4:39 pm

Post » Thu Jun 21, 2012 10:46 pm

Not sure what CDCooley uses. I use PEX Decompiler http://skyrim.nexusmods.com/mods/2909. There is also a decompiler linked in uesp pages. Should you want to code 'old style' you can found PEX Compiler at the nexus http://skyrim.nexusmods.com/mods/4058 (I used to edit script with that before the CK go public. It needs some adjustments includding Hex editting to made the pex file work. You can find instructions in the file's thread -there are not much posts- )
User avatar
candice keenan
 
Posts: 3510
Joined: Tue Dec 05, 2006 10:43 pm

Post » Thu Jun 21, 2012 5:49 pm

That code that I posted uses an official tool to decompile PEX tools, which I first saw mentioned by SmkViper, so it'd be my first option.

That said, I haven't tried it and can't try it right at the moment to see how easy it is to use. If I remember correctly its usage isnot documented on the wiki.

Cipscis
User avatar
Samantha hulme
 
Posts: 3373
Joined: Wed Jun 21, 2006 4:22 pm

Post » Thu Jun 21, 2012 5:23 pm

That code that I posted uses an official tool to decompile PEX tools, which I first saw mentioned by SmkViper, so it'd be my first option.

That said, I haven't tried it and can't try it right at the moment to see how easy it is to use. If I remember correctly its usage isnot documented on the wiki.

Cipscis
Just tried. The output is better shorted than the one of the tool I linked. Otherwise the code is pretty similar. Tried reassembling but didn't found a way yet.

EDIT: Yay!!! I managed to reassemble the pex file!!!! :biggrin: This tool is way easier to handle :smile:
User avatar
Vahpie
 
Posts: 3447
Joined: Sat Aug 26, 2006 5:07 pm

Post » Thu Jun 21, 2012 5:53 pm

Yes, I was using the Papyrus Assembler with the -D option as suggested by SmkViper. There is no documentation, but from the command line just call the program with no arguments and it will give which flags it accepts. The official compiler has some interesting options too. You can see each stage of the compilation and optimization process.

The optimization for local auto properties makes them 100% equivalent to local variables if your talking about basic types (int, float, bool, etc.). And SmkViper's description explains the results JustinOther posted perfectly. Isn't it nice when theory and practice actually match.
User avatar
Red Bevinz
 
Posts: 3318
Joined: Thu Sep 20, 2007 7:25 am

Post » Thu Jun 21, 2012 4:50 pm

Hi, very interesting post. Thanks for sharing :)

Anyone have better alternatives for the following lines?

Int attackerEquipWeaponType = akAttacker.GetEquippedItemType(1)

Weapon w = akAttacker.GetEquippedWeapon()

Float attackerLevel = akAttacker.GetLevel()

Float aHealth = akAttacker.GetAV("health")

If (akTarget.WornHasKeyword(ArmorHeavy))
;DoSomething
EndIf
User avatar
Assumptah George
 
Posts: 3373
Joined: Wed Sep 13, 2006 9:43 am

Post » Thu Jun 21, 2012 1:50 pm

I'm still missing something... I tried the "papyruscompiler test.pex -D", didn't work, added the path, moved the file (scripts/ folder, data/ folder, c:\a\), altered the scriptcompile.bat by adding the -D (which I use in Notepad++... finally got THAT working)... nothing. It always tells me it can't find the test.pex file. I don't really have much use for it, I'm just curious to see it work and how well.

aBreton: sorry.. nope. It looks pretty straight-forward to me. The only thing that MAY help, and I haven't tested this (and I don't think it's been posted, but I could be wrong again)... but maybe 'declaring' the variables at the top of the script and not during runtime may help? eg: [Float aHealth = akAttacker.GetAV("health")] would be [float aHealth] at the top, then in your event/function/whatever you'd fill that variable [aHealth = blahblah].

I'd be curious to know if this works.... it's the way I do it most of the time, but just to keep things neat and so I can see what variables are inside my scripts without hunting for them... it never occurred to me that it could be faster doing like that.

[EDIT: I haven't tried it yet, so cannot attest to its efficacy or bugless-ness, but I just found the Skyrim Performance Monitor.. http://skyrim.nexusmods.com/mods/6491. It may help in this research.]
User avatar
Floor Punch
 
Posts: 3568
Joined: Tue May 29, 2007 7:18 am

Post » Thu Jun 21, 2012 2:20 pm

I'm still missing something... I tried the "papyruscompiler test.pex -D", didn't work, added the path, moved the file (scripts/ folder, data/ folder, c:\a\), altered the scriptcompile.bat by adding the -D (which I use in Notepad++... finally got THAT working)... nothing. It always tells me it can't find the test.pex file. I don't really have much use for it, I'm just curious to see it work and how well.

Just tried a bit and found a way:

1. Make sure the wanted .pex-file is in the PapyrusCompiler-directory.
2. Edit the ScriptCompile.bat and add the following lines:

PapyrusAssembler test -Dpause

3. Make sure you don't add the extension (.pex), because it only wants the file's name.
4. If the Compiler doesn't bring out an error, then there should be a new file in the directory. Open it with a textprogram (I recommend Notepad++).

For everyone who wants to try it out.


@ aBreton: The only thing I would recommend, but SKSE, is using

 Weapon atWeap = source as weapon

instead of getequippedweapon(), only if you use the OnHit-Event of course, but it seems so. The vantage of this method is that you are really getting the weapon used by the attacker. Otherwise you don't really know if he used his left hand or right hand. I use this method too and I can really recommend it. In addition to that getweapontype() from SKSE could give you the weapon type easily without wasting too much code.



Kahmul
User avatar
Allison C
 
Posts: 3369
Joined: Mon Dec 18, 2006 11:02 am


Return to V - Skyrim