-
Notifications
You must be signed in to change notification settings - Fork 85
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
CitizenManager
extension desync when Manager gets patched by other mods
#1599
Conversation
…zen access by id by adding ref param where possible without many changes
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.
Thank you for taking care of this!
No more isolated static references and therefor no more cache misses
no more possibility of stale references
no more having to rely on static initializer invocation
by removing the extension method we should save some time due to avoid the method call overhead.
lgtm
In |
@krzychu124 this is a big change. We have similar code for NetNode, NetSegment, Vehicle, ... . if we want to change all of them its going to be uncomfortable! why not to do this? private static CitizenUnit[] _citizenUnitBuffer => Singleton<CitizenManager>.instance.m_units.m_buffer; One downside is when it is used inside a loop then accessing Another solution is to update the static cache before every simulation step. that way we can maintain fast code and we wouldn't be passing ref around (not sure how fast ref arguments are). A third solution is to ask other mods to update Mangers buffers when level is preloaded. then TMPE can simple update the static cache when level starts. I was aware of this potential issue (of someone expanding the arrays) when I introduced the strategy of caching |
I don't plan changing any of those since there are unlikely to change and for Vehicles/ParkedVehicles More Vehicles mod replaces the ref when mod is enabled.
It's the worst possible option because it hides the "expensive" call behind the method and in most cases you can just put array ref in a variable and reuse it.
No idea what are you talking about here. Objects are passed by ref. Updating cache every step means doing stuff you don't have to do if you would just use ref when needed.
They can't use Singleton<>.instance is most expensive call (result from the mono profiler I've managed to setup yesterday) Time(ms) Count P/call(ms) Method name
########################
14938.000 159775 0.093 ColossalFramework.Singleton`1::get_instance()
... |
I mean like this: namespace TrafficManager.Util.Extensions
{
using ColossalFramework;
public static class CitizenUnitExtensions {
internal static OnBeforeSimulationStep() => _citizenUnitBuffer = Singleton<CitizenManager>.instance.m_units.m_buffer;
private static CitizenUnit[] _citizenUnitBuffer = Singleton<CitizenManager>.instance.m_units.m_buffer;
internal static ref CitizenUnit ToCitizenUnit(this uint citizenUnit) => ref _citizenUnitBuffer[citizenUnit];
}
}
Does deserialization release |
I'm aware what you meant and I don't like it/recommend anyone doing this.
They could but they don't. Case-closed. |
…s-Traffic-Manager-President-Edition into bugfix/citizen-manager-array-desync � Conflicts: � TLM/TLM/TLM.csproj
I need two reviews 😉 |
@@ -2059,7 +2071,7 @@ private ExtSoftPathState | |||
parkRot, | |||
citizenId)) | |||
{ | |||
citizenId.ToCitizen().SetParkedVehicle(citizenId, parkedVehicleId); | |||
CitizenManager.instance.m_citizens.m_buffer[citizenId].SetParkedVehicle(citizenId, parkedVehicleId); |
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.
Is this better? Thought we upgraded most/all direct uses of m_ buffers to helpers?
Also in several other locations, m_ buffers are used
I get it this is to always get the latest ref which may be modified by some mods, but we can update the ref ourselves too.
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.
Yes we could update ref ourselves but I see a few problems.
- When? Between user hit "load" on a savegame to the frame you see in-game screen is a ton of frames where other mods could change ref and ref swap cannot be reliably detected - working on desynced refs will cause weird bugs, assuming we will notice that something is wrong.
- There is no need to update ref once game loads everything and show the in-game UI.
- It's hard to benchmark what is the real cost and potentially using cached ref can cause more performance issues than accessing it when needed (in range of method call)
The other managers (Net/Building) are really hard to mod so I doubt anyone try that in near future. Still, there are certain ways how you can do ref swap without breaking anything, although much harder and limits how some things can be handled (e.g. deserialization).
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.
It's the worst possible option because it hides the "expensive" call behind the method and in most cases you can just put array ref in a variable and reuse it.
I could be wrong on this but I don't think all code should be optimal out of the fear it hides performance issues and mistakenly used in future. I think only the critical path should be optimized.
other than that code looks good.
…nto bugfix/citizen-manager-array-desync � Conflicts: � TLM/TLM/Manager/Impl/UtilityManager.cs
ref param
where possible and feasibleMAX_CITIZEN_COUNT
andMAX_INSTANCE_COUNT
in loops with a call for current array size (I've heard about some mod(s), still early, WIP, which could lift the game limits, so fixes possible compatibility issues ahead of time)