-
-
Notifications
You must be signed in to change notification settings - Fork 377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: run SaveGameStats in a job #5934
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial feedback - instead of adding the new LuaEvent class to piecewise construct an argument table, construct a LuaTable object inside of l_game_savegame_stats_callback
, add values to it as normal, and pass that LuaTable handle as the args parameter to LuaEvent::Queue
. (Don't forget to dispose of the underlying table from the stack - a LuaTable doesn't auto-pop its table.)
If that doesn't currently compile, let me know and I'll do the template metaprogrammery to get it to work.
A note - we tend to reserve the l_<namespace>_<method>
naming convention for functions following the Lua_Function
API prototype, i.e. static int l_name(lua_State *l)
. This is just a "regular" callback and can have any particular name.
Regarding profiling, we have an in-process profiler for C++ which can be used via the PROFILE_SCOPED()
macro (with the appropriate include of profiler/Profiler.h
). Details on usage are in the dev docs.
Thanks for working on this! We unfortunately don't have good (or any) Lua-side profiling utilities at current, but I'll pull the branch and take a closer look to see what's responsible for the stutters. |
Finally, as a suggestion - you may want to move the JobSet into a more central API like |
Thank you for having a look and providing feedback!
I'm afraid I still have extremely limited knowledge of how the Lua stack works and what can and can't be constructed on it and passed around. It's not really made any clearer (to me) by the wrappers you guys have developed which have various restrictions such as the ScopedTable. If there is a book or other training material you can point me at so I can learn more about it that would be appreciated. So I didn't think a LuaTable could be constructed on the stack and then passed around and modified etc before invoking a Lua function while other parts of the code also potentially modify the Lua stack (ie, another Job in another thread). Inside In the "normal" functions the This is why I made that LuaEvent class to construct the table and manage the
???
Noted.
I saw the macro, but haven't had time to look into it. Thanks for the link, I'll have a read. |
Yes please. No need for me to reinvent the wheel.. |
This is partially why the Lua*** wrappers were developed, to simplify interacting with Lua from the programmer's perspective. I'm sorry they're not clear and easy to use either - we (at this point, I) probably should write a primer on interacting with Lua and add it to the dev docs.
Lua is implicitly and explicitly single-threaded only. Only the main thread is allowed to call any Lua API functions on the main Lua state ( I should have mentioned this when I was originally describing the problem to you, sorry. 😅 The Other Lua states may be constructed for use on separate threads, but they operate under a share-nothing model in relation to the main Lua state from both a C and Lua-side perspective. As mentioned in another reply, from the "main" thread you can * Startup and shutdown excluded, for obvious reasons.
Don't worry about it at all - this is why I'm taking the time to review and provide feedback in such depth on your PRs. My hope is to help smooth along the learning process; feel free to mention me (as sturnclaw) in IRC with questions if that's a more expedient way to learn.
Here's an annotated example: // Create a new table on the Lua stack.
// This is a shorthand to call lua_newtable(l); LuaTable(l, -1).
// Passing a stack index to the constructor instead aliases an existing table.
LuaTable tab { l };
// Interact with the table as normal, don't need to bother with the underlying Lua API.
tab.Set("myKey", myVal);
// Pass this table to a Lua event. The LuaTable wrapper aliases the underlying table on
// the Lua stack when copied.
// LuaEvent::Queue pushes all parameters to Lua and then destroys its arguments. You
// generally don't have to worry about lifetime concerns as long as you're not passing
// a ScopedTable handle.
// LuaEvent::Queue is guaranteed to leave the stack in the same state as when you called it.
LuaEvent::Queue("myEvent", tab);
// Because LuaTable aliases a table on the stack, it does not remove that value from
// the stack when it is destroyed. Instead, you have to manually call lua_pop() to remove
// the table implicitly created via LuaTable(lua_State *l).
lua_pop(l, 1); |
Sure; I first wanted to see if it would work at all so just hacked it into the I moved the Job itself into the I moved the jobset into The issue is that the |
Looks good. Naming is a little stilted, I might personally try to name it something like
More seriously, this isn't ideal. I suspect the editor will likely crash on startup because of this, as Preferably, the |
This is proving to be marginally challenging as the other Applications do not have any Job Queues.. |
pioneer/src/core/Application.h Line 89 in 2884328
|
The idea is to run this expensive task in an asynchronous job and notify the Lua side once it is completed. This is to prevent the stutter from occurring every time the SaveGameStats API is called from Lua. In practise, the jobs all seem to complete almost instantaneously which then results in a mass of LuaEvents sent to Lua at the same time. So instead of a set of small stutters, the interface experiences one big stutter. So it appears as if the cause of the stutter wasn't the disk access and JSON parsing in the C++ side, but the logic invocation and data transfer between C++ and Lua. As always, it's better to profile first and optimise later..
- Simple manual profiler for checking the time cost of certain Lua methods - Add Engine.nowTime, which returns the exact time since an unspecified epoch at the moment it's evaluated
Remove the superfluous LuaEvent class and instead construct the LuaTable directly in the callback. Also pass Json objects by reference to avoid expensive memory copies.
* Remove debug output * Comment out profiler * Optimise lua table access * remove dead-code
The jobset has been moved into the Lua::Manager class and can generically run any job on the "Lua" job queue. The job has been moved into the SaveGameManager class so that any code can load game data as Json in a job.
Require a JobQueue to instantiate a LuaManager object. This removes the requirement to include "Pi.h" in LuaManager.cpp and ensures that the other applications (Editor) can also run.
20a7010
to
63c95d1
Compare
No idea how or why I missed this.. I was grubbing around inside the implementation files and must have missed the base class. Proposed fix in 63c95d1 |
Fixed in 1a871c8 |
Looks good. Will give this a whirl tomorrow. |
##' Bugs / Feature requests
Attempts to fix #5929
Background
The idea is to run this expensive task in an asynchronous job and notify the Lua side once it is completed. This is to prevent the stutter from occurring every time the SaveGameStats API is called from Lua.
Details
The code was modified to call the supposedly expensive part of the SaveGameStats API (loading and parsing the game file) into a Job. Once finished, the data is marshalled into a LuaTable and a LuaEvent is triggered to pass the data to the Lua side.
The Lua side registers an Event listener, which parses the data and updates the SaveGame cache entry for the data.
In practise, the jobs all seem to complete almost instantaneously which then results in a mass of LuaEvents sent to Lua at the same time. So instead of a set of small stutters, the interface experiences one big stutter.
So it appears as if the cause of the stutter wasn't the disk access and JSON parsing in the C++ side, but the logic invocation and data transfer between C++ and Lua.
As always, it's better to profile first and optimise later.. that said, I learned a lot.