-
Notifications
You must be signed in to change notification settings - Fork 39
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
Improve path cache generation performance and accuracy, and add prepend mounting #93
base: dev
Are you sure you want to change the base?
Conversation
Some kind of |
I was reminded of this when I mentioned that I'd get some significant chugging on startup if I had several archive files (the more archive files, the longer times). I took a look and found that it was redundantly traversing through already-processed file structures. This PR fixed that for me. I can't speak for the MacOS piece to this, however. |
I somewhat forgot that I never elaborated on that. Yes, it's as Eblo says. When we enumerate a directory, PhysFS gives us everything in each mount point, including duplicates. If that duplicate item is a directory, then we end up traversing it multiple times, which gets exponentially worse the more mounts have that directory path. If one of those directories has a lot of files in it, then the wait time gets to be very noticeable. I saw a small error in the MacOS commit while I was working on #163, and then had an idea on how I could do the optimization even better, so here's an update! Now with path cache enabled we keep the mounts separate. PhysFS enumerates through the mounts one by one anyway, so there shouldn't be any performance penalty from this. This also guarantees that the highest priority file is chosen, even if the directory path has different cases in different archives. Additionally, once I get around to adding a mount_prepend method for dynamically loading patches, it'll be trivial to have it simply update the path cache instead of completely reloading it like the reload option on mount does. I kept the previous commit so that this one could be easily reverted if desired, but I can squash them if you want. |
I gave this some more thought, and decided I really don't like the dynamic mount point hack from my last push. I experimented with unmounting and remounting everything, but decided that was unacceptably slow. So instead I looked into patching physfs, and found out that the change I wanted was pretty simple to implement. I'll update this PR again after mkxp-z/physfs#1 has been merged. |
I finally got around to polishing my latest bit of feature creep for this PR, so I'm pushing this now for anyone interested to look at. It's currently set to pull from my PhysFS fork.
|
Are your PhysFS changes something that perhaps upstream would be willing to consider merging? |
#181 might make it easier to test this PR. |
Operating systems allow file paths to be at least a kibibyte in length. Developers are unlikely to use anywhere near that much, but we may as well allocate for it just in case.
Alright, here's a test suite for you. The ruby script has plenty of comments talking about what's going on. I also fixed a segfault I accidentally introduced when mounting a nonexistent path and changed |
In games that have a lot of files and an RTP is being loaded, this can save a couple seconds. This also keeps the cases of archives with lower priority from overriding the higher priority archives.
This commit requires mkxp-z/physfs#1, which allows us to enumerate files in specific mount points. This allows us to guarantee that the highest priority file is chosen, even if the directory path has different cases in different archives.
System.mount now accepts a fourth argument to prepend the mount to the search path instead of append it. In addition, specifying the fourth argument when the third argument is true will update the path cache instead of triggering a complete reload. This commit also adds a System.regenerate_scripts method, which regenerates the $RGSS_SCRIPTS array, for dynamically loading patches. This method is a no-op after the preload scripts have finished executing.
…rgument. While this could potentially cause problems if the game was hoping to catch changes to loose files, that case seems highly improbable.
I've played around with the test script myself and decided to address a few more performance issues, mostly in the PhysFS PR. First off, I noticed that enumerating 7zip archives was taking longer than I thought it should, and that if we'd already enumerated it once then reloading the path cache would take even longer. Digging into the PhysFS code, I saw that their hash table is always allocating 64 buckets - which meant the 30k test file had an average of ~469 items per bucket. Now I have it set to generate enough buckets to have 8-16 items per bucket, with a cap of 64k buckets should someone decide to load an archive with over 1 million entries in it. I also added a Next, I addressed some terrible performance issues in mounting and initially enumerating zip archives. The enumeration problem was caused by I also included a test for the NFC/NFD thing. It'd be nice if someone with a more modern version of MacOS could confirm the path normalization is still converting it. Here's my output from the current version of mkxp-z: Current output
And here's the output from this PR: This PR's output
Into the stable branch we're currently using? Almost definitely not. It's been feature frozen for a while now. As for the 4.0 branch... maybe. Hrm. And it looks like I force pushed this a bit too soon and it didn't catch the updated PhysFS branch. I'll try to retrigger the builds later if you don't do it first. |
Very impressive optimization wizardry!
I'll close and re-open now, no worries. |
Nope, still broken. You'll probably have to clear the caches first. https://github.com/mkxp-z/mkxp-z/actions/caches |
Cleared the caches, let's try this again. |
Small correction, introducing that buffer dropped mounting time from ~3 seconds. The other ~1.5 seconds saved was from the hash bucket change, although I don't know why it had such a big impact on mounting. It's still nice to see the time get that low, though. |
Just fixed a bug in the linked PR that would have prevented mounting zip64 archives. It won't affect any of the test archives, so you shouldn't need to trigger a rebuild for it. Definitely clear the caches again before merging, though. |
What was originally a fix for loading files with Japanese characters and trying to stop redundant path traversal in the path cache has ballooned quite a bit in the past few months. Here's the salient points.
System.mount
now accepts a fourth parameter to prepend the mount to the search path, giving it higher priority than everything currently mounted.System.mount
with the reload parameter set to true or unspecified now only updates the path cache instead of triggering a full reload unlessSystem.mount
orSystem.unmount
was called with reload set to false since the last path cache generation. This also requires the new PhysFS functionality.System.regenerate_scripts
has been added to simplify reloading the scripts array when prepending mounts that override the scripts file. The method only functions while preload scripts are still running and is a no-op later.load_data
now attempts to use the path cache to access files (it apparently didn't previously).Fixes #92