RaidersClamoring Posted December 17, 2022 Share Posted December 17, 2022 I know long-running functions are a problem but how much faith should I put in the compiler making the scripts lightning fast? Will the length of the actual script add burden to the engine and/or more than a few milliseconds to execution time? The functions are all globals. Link to comment Share on other sites More sharing options...
SKKmods Posted December 17, 2022 Share Posted December 17, 2022 Length of script is irrelevant, its still exactly the same as managing compute/memory/storage resources on mainframes in the 1960s (just add a render pipeline); - Properties and persistent script variables use up limited script memory heap (War of the commonwealth found the actual limit). - Script function calls use the limited compute time (mostly) in between frames. - Non native event registrations load up the event broker. Whether they are in one massive 2000 line script or chunked into 10 scripts of 200 lines each the same resources are consumed. Blocking functions (Utility.Wait), multi-threading and maintenance are good reasons to chunk up a large script. There are no hard rules on this, only experience and performance test observations. People sometimes ask me for advice and when I try to explain they need to cut a bunch of their content as the cost is not worth the envisiaged 'mursive benefit they think I'm mad as apparently mods are supposed to hoover up as much resource as they can and trash save games. Link to comment Share on other sites More sharing options...
RaidersClamoring Posted December 18, 2022 Author Share Posted December 18, 2022 Length of script is irrelevant, its still exactly the same as managing compute/memory/storage resources on mainframes in the 1960s (just add a render pipeline); - Properties and persistent script variables use up limited script memory heap (War of the commonwealth found the actual limit). - Script function calls use the limited compute time (mostly) in between frames. - Non native event registrations load up the event broker. Whether they are in one massive 2000 line script or chunked into 10 scripts of 200 lines each the same resources are consumed. Blocking functions (Utility.Wait), multi-threading and maintenance are good reasons to chunk up a large script. There are no hard rules on this, only experience and performance test observations. People sometimes ask me for advice and when I try to explain they need to cut a bunch of their content as the cost is not worth the envisiaged 'mursive benefit they think I'm mad as apparently mods are supposed to hoover up as much resource as they can and trash save games. That's some quality information, thanks. If I were to put all of these in one script it would contain 4k lines or more. I should probably err on the side of caution and chunk it up a bit. Link to comment Share on other sites More sharing options...
SKKmods Posted December 18, 2022 Share Posted December 18, 2022 My longest script is 3000 lines. Longest base game script is CompanionAffinityEventQuestScript at 14500 lines. Link to comment Share on other sites More sharing options...
niston Posted December 18, 2022 Share Posted December 18, 2022 Reasons why I split up my scripts: #1 Separation of Concerns: Each script has a narrow, well defined scope of function/action. The script caters to this function/action only, and does absolutely nothing else.#2 Maintainability: Scripts made according to #1 will be easier to maintain than a "god script" that does "everything" with 30k lines of code.#3 Reusability: If you have a generic script that applies matswaps to references but does absolutely nothing else, you can reuse that script whenever you want to apply matswaps to references - even in other mods.#4 Performance: Generally, a script can run only one thread at a time. You can run one thread each on multiple instances of the same script though. Papyrus can go really really fast if you do this, but it's tricky. Link to comment Share on other sites More sharing options...
RaidersClamoring Posted December 19, 2022 Author Share Posted December 19, 2022 Reasons why I split up my scripts: #1 Separation of Concerns: Each script has a narrow, well defined scope of function/action. The script caters to this function/action only, and does absolutely nothing else.#2 Maintainability: Scripts made according to #1 will be easier to maintain than a "god script" that does "everything" with 30k lines of code.#3 Reusability: If you have a generic script that applies matswaps to references but does absolutely nothing else, you can reuse that script whenever you want to apply matswaps to references - even in other mods.#4 Performance: Generally, a script can run only one thread at a time. You can run one thread each on multiple instances of the same script though. Papyrus can go really really fast if you do this, but it's tricky. Makes sense. One thing I still don't understand though is how threads are allocated to global function calls, with them not having a Self. Will the engine still notice and usher them all into the same thread if the processes are concurrent and from the same script? Link to comment Share on other sites More sharing options...
SKKmods Posted December 19, 2022 Share Posted December 19, 2022 Consider each script instance as one thread of execution. https://www.creationkit.com/index.php?title=Threading_Notes_(Papyrus) A global script which contains only global functions will be one thread, the "self" is probably a NUL ScriptObject as it cant hold any persistent variables or event registrations. Link to comment Share on other sites More sharing options...
lee3310 Posted December 21, 2022 Share Posted December 21, 2022 (edited) Consider each script instance as one thread of execution. https://www.creationkit.com/index.php?title=Threading_Notes_(Papyrus) A global script which contains only global functions will be one thread, the "self" is probably a NUL ScriptObject as it cant hold any persistent variables or event registrations.Very useful information, never took the time to understand threading, glad i did now.PSCan you clear something for me about latent functions ?if i do something like this: bool myBool = false myBool = Myquest.Start() If myBool ;do something EndIfDoes the script wait for the start() to return before checking "if true/false" or myBool will be "false" if the quest doesn't start in time ?I've seen people using a while loop to start a quest and i don't feel confortable with it: While !MyQuest.isRunning() utility.wait(0.1) MyQuest.Start() EndWhile Edited December 21, 2022 by lee3310 Link to comment Share on other sites More sharing options...
SKKmods Posted December 21, 2022 Share Posted December 21, 2022 No a script will not wait for an IF evaluation to be true, it is a point-in-time test. Unbounded while loops are pure filth, really nasty as they block the whole script and constantly consume valuable script execution ticks in that case every ~6 frames and could do in perpetuity as they are Unbounded. Use an event, either standard system or create a custom event or start a real time timer for a regular test. In this case there are several system events that can be used as elegant triggers; Self.RegisterForRemoteEvent(myQuest, "OnQuestInit") OR Self.RegisterForRemoteEvent(myQuest, "OnStageSet") ; if the quest actually sets stages, some dontWhich could look like this; If (myQuest.IsRunning() == False) Self.RegisterForRemoteEvent(myQuest, "OnQuestInit") Else DomyQuestRunningStuff() EndIf Event Quest.OnStageSet(Quest akSender) If (akSender == myQuest) Self.UnRegisterForRemoteEvent(myQuest, "OnQuestInit") DomyQuestRunningStuff() EndIf EndEvent Function DomyQuestRunningStuff() ;stuff EndFunction Link to comment Share on other sites More sharing options...
lee3310 Posted December 21, 2022 Share Posted December 21, 2022 (edited) No a script will not wait for an IF evaluation to be true, it is a point-in-time test. Unbounded while loops are pure filth, really nasty as they block the whole script and constantly consume valuable script execution ticks in that case every ~6 frames and could do in perpetuity as they are Unbounded. Use an event, either standard system or create a custom event or start a real time timer for a regular test. In this case there are several system events that can be used as elegant triggers; Self.RegisterForRemoteEvent(myQuest, "OnQuestInit") OR Self.RegisterForRemoteEvent(myQuest, "OnStageSet") ; if the quest actually sets stages, some dontWhich could look like this; If (myQuest.IsRunning() == False) Self.RegisterForRemoteEvent(myQuest, "OnQuestInit") Else DomyQuestRunningStuff() EndIf Event Quest.OnStageSet(Quest akSender) If (akSender == myQuest) Self.UnRegisterForRemoteEvent(myQuest, "OnQuestInit") DomyQuestRunningStuff() EndIf EndEvent Function DomyQuestRunningStuff() ;stuff EndFunction Much better thank you. I just have to split my function in two and call the second part based on IsRunning() result.It's a bit tricky when the function which starts the quest has parameters (you have to use external declarations) Edited December 21, 2022 by lee3310 Link to comment Share on other sites More sharing options...
Recommended Posts