TrevorSundberg Posted January 5, 2015 Share Posted January 5, 2015 I'm trying to understand why Papyrus is so slow. I've used both vanilla Skyrim.ini and I've also modified it to allow extra task time with no real change in performance. I'm fluent in C++ and I understand this is script, but even a really really basic VM runs orders of magnitude times faster than Papyrus. What in the hell are they doing internally? Calling 'GetPositionX' 20 times should not take 300 milliseconds. I implemented a horrendous 'GetPositionX()' in PHP (one of the slowest languages around) which is printing 100 times per call, and even then at worst it takes 0.6 milliseconds (http://pastebin.com/2EA3wH2c). I can only imagine this has something to do with thread synchronization. I also know that the Utility.GetCurrentRealTime and Debug.Trace could be taking up time, but the time scales pretty much linearly with the number of Player.GetPositionX() I put in. Anyone care to shed some light on this? And is there any way to speed it up?Float lastTime = Utility.GetCurrentRealTime() While True Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Player.GetPositionX() Float currentTime = Utility.GetCurrentRealTime() Debug.Trace("Time MS: " + (currentTime - lastTime) * 1000) lastTime = currentTime EndWhile[01/05/2015 - 10:36:45AM] Time MS: 300.000183 [01/05/2015 - 10:36:45AM] Time MS: 334.000580 [01/05/2015 - 10:36:45AM] Time MS: 316.999451 [01/05/2015 - 10:36:46AM] Time MS: 333.000183 [01/05/2015 - 10:36:46AM] Time MS: 317.000397 [01/05/2015 - 10:36:46AM] Time MS: 283.999451 [01/05/2015 - 10:36:47AM] Time MS: 317.000397 [01/05/2015 - 10:36:47AM] Time MS: 316.999451 [01/05/2015 - 10:36:47AM] Time MS: 317.000397 [01/05/2015 - 10:36:48AM] Time MS: 300.000183 [01/05/2015 - 10:36:48AM] Time MS: 299.999237 [01/05/2015 - 10:36:48AM] Time MS: 333.999634 [01/05/2015 - 10:36:49AM] Time MS: 350.000366 Link to comment Share on other sites More sharing options...
Mattiewagg Posted January 5, 2015 Share Posted January 5, 2015 Afraid not. Papyrus is really awful in terms of optimization. See this thread for more info: http://forums.bethsoft.com/topic/1409991-best-practices-papyrus/ Link to comment Share on other sites More sharing options...
cdcooley Posted January 5, 2015 Share Posted January 5, 2015 On 1/5/2015 at 7:04 PM, TrevorSundberg said: I can only imagine this has something to do with thread synchronization. I also know that the Utility.GetCurrentRealTime and Debug.Trace could be taking up time, but the time scales pretty much linearly with the number of Player.GetPositionX() I put in. Anyone care to shed some light on this? And is there any way to speed it up?You've got it. Any function that has to interact with another game object will need to get an exclusive lock on that object. So each of those calls releases the lock on your quest and gets one on the player object (which happens to be the most used object in the game). The speed of those calls are limited by framerate (which is apparently about 60 FPS in your case). Calling GetPositionX in a script attached to a particular object is very fast, but calling it against some other object is slow. Most functions are going to be like that although there are a handful of non-latent functions that are always fast. Check the CK wiki for the list of those. Papyrus isn't intended for complex real-time programming. Every access to an external object from your script will cause a delay, so cache values where you can, make use of events, and always remember that Papyrus is just a way to access and manipulate game content. Papyrus is just one component of the game engine and so not only are your scripts competing with other scripts but also with the AI, physics, and graphics components of the game. Even if you could increase Papyrus performance it would come at a terrible price. Link to comment Share on other sites More sharing options...
TrevorSundberg Posted January 5, 2015 Author Share Posted January 5, 2015 Thanks for the input. I just tested calling my own GetPositionX, and sure enough it was way faster (17ms, which is still slow as hell, but much better). In truth, I would love to use Script Dragon here because I need access to a bunch of the functions that Papyrus has access to, and that would let me run at native speeds... but Script Dragon keeps randomly crashing, and SKSE doesn't seem to provide the functionality to call Papyrus functions from C++. What do I do ;_;? Link to comment Share on other sites More sharing options...
Terra Nova Posted January 5, 2015 Share Posted January 5, 2015 http://www.creationkit.com/Category:Non-delayed_Native_Functionhttp://www.creationkit.com/Category:Latent_Functions Link to comment Share on other sites More sharing options...
Arocide Posted January 6, 2015 Share Posted January 6, 2015 (edited) The reason for papyrus being slow is due to how Papyrus and the engine differ. Papyrus is Real-Time, The Engine it communicates with is per-Frame. Sadly Papyrus doesn't stack function calls so it can only perform a single 'Delayed' Function call per stack (Script Instance) per frame. So Papyrus itself runs at a 'ok' speed for what you need most of the time, it's just how it interfaces with the engine that is slow. For that reason and well a few others it's recommended you multi-thread and cache (as cdcooley suggested) where you can. Edited January 6, 2015 by Arocide Link to comment Share on other sites More sharing options...
Recommended Posts