Performance Impact Question
Just a quick question what the best method for picking items off belts is, when completing custom recipes. I'd guess using the native manufacture implementation would be the best. Looking at an ancient copy of refined power they do something like this Which I think can be optimized a bit... but if every single building is running O(n) loops... I figure this will get pretty slow pretty fast. I could improve on this by not looping if we already have an item in the building, so we don't need to check if the item on the belt is in our allow list, and instead just need to validate if it is what is loaded. Similarly this function can be skipped entirely if our inventory is full. Simple stuff like that... I'm sure a more recent version of refined power has much more sophisticated code...
27 Replies
Looking at nogs mods we're doing something like this The nested if's here could use some love... but at lest this runs O(1). Still tho, that is a lot of logic on every single tic... Does anyone have an answer? I understand I may be way out in uncharted water... but I figure that I should at least poll the community to figure out what has been done before me. Maybe there is a way to hyjack the manufacturer code?
I did try to hyjack that code... by injecting item slots into the input and output inventor by overloading the setRecipe function... I hoped to be able to add custom addtional items a manufacturer can consume. In my case fuel and byproducts.
My next thought is to try and implement my manufacturers as two manufacturers. Each with a settable recipe. I think this could be accomplished by making a buildable, that contains two child actors of type Buildable Manufacturer. The trick would be in the widget and how the components are assigned or exposed to the child actors. But I havent made it too far down this path of research becasue I wanted to see what y'all have come up with first. Maybe there is an easier path forward.
It doesn't look too bad, three of the if's are just checking if things are valid
Though one of the
GetStorageInventory()
calls is redundant (already checked earlier)I dont think GetStorageInventory has any performance impact because it should be FORCEDINLINE replaced with a pointer
I'm just noting that the return value is checked twice
How did you make that colored??
Oh neat... I totally missed that, thanks.
Problem with this implementation is that it doesn't validate inputs just kinda sucks stuff up'
I guess that'll work fine tho... because we can validate once it's in the inventory
its a depatrure from Satisfactorys wa of doing things... but... it might be more efficent?
For items there's some
peek
functionDo we know the logic tic per second? In starcraft the logic tics are 23.81 updates per second, while graphics can draw at whatever they want.
ĀÆ\_(ć)_/ĀÆ
I'd suggest doing things in the same way as the game does them, that is to have your buildables pull in resources from connections in
Factory_Tick
I think that is essentially required, as factory tics operate in parallel, and dont sleep... unlike tics. Im just thinking, if I have 4x 1200 item per minute belts... thats 20 items per second per belt. If we are pulling in one, and only one... at a time, because we can create sushi belts... Then I need to be checking inputs at least 20 times per second... per building. How I do that if you have lets say 100 buildings with four inputs each... 8000 checks per second. I'd think... this would eventually fall apart.
They run in parallel but still in lockstep with the game thread
VS has a built-in profiler, you can use it to see if your code is slow
Sometimes it's not your code per se, it's the functions you call from your code.
Google seems to think we are operating at 60 Factory Ticks per second. Which would imply, if my maximum input is 20 items per second... that I am checking 3 times as often as I need to. Alright... with that knowledge in mind... I'll write up some basic functionality, and if we hit performance concern, I should be able to speed it up 3x at least... by dividing the machines up into three groups. Thanks Rex! Will do my best
I'm confused, where would Google get those numbers from
The phrase "Google seems to think" implies... I googled it... and clicked a bunch of links... and read a bunch... until a number fell out.
š Sorry... bad habbit. Formed because Dr. Google is a common phrase I like to use when communicating with medical professionals because it implies that I understand that google research isn't as accurate as a medical opinion while also articulating that I did some research. Instead of something like.... but I read on this website or that website or... doesnt that mean this because that, etc. suggesting Im second guessing their professional option... Which some don't appreciate... Im just articulating confusion and curiosity and a willingness to learn and understand.
I didn't read the paragraph above. My point is, how reliable is that number?
Do you need to account for it when making a machine?
Not very reliable, intend on testing. In theory, as we have a delta time variable in FactoryTick. as soon as I drop that somewhere readable we should have an answer what our upper bound is. In theory... this function should be called n times, where n is the number of times that tick should have fit inside of delta time. Ideally this will always be 1. However as the computational load grows, and tics are missed, additional ticks should be issued inside of one tick block to keep the logic constant. But... that is all academic until I test it. I just feel better starting down this path knowing that there are optimizations I can do, on my side... to help alleviate performance issues. If I start down this path with no options available... I might be better off trying to figure out how to leverage CSS implementations, as anything I do would just be worse.
@Rex [they/them] Based on our pass by reference conversation, then it would appear RRD's implementation of CollectItems isn't great because we are using CanStoreItemInInventory, which looks like this Which would imply on every Factory Tick for every single input for every single building, we are making a copy of a stack. I'm thinking the less memory copying the better. Is my analysis about right?
Which implies my implementation isn't going to work either... as I'm copying one or more FInventoryItems per input per factory tick on every building. I haven't found a way to just grab a pointer to an inventory so I can just look at it without the copy.
My implementation might actually be worse, because I am copying more then one FInventoryItem. A minimum of two, one from the peek, and one from the grab. Plus up to N copies where N is the number of OutItems our connection can see. But on the other hand, I am conducting fewer GrabOutput calls. Im checking if the items we can grab are on the allowed list... where as the example code tries to grab each of the allowed items from the input. But I guess that means that theirs will be faster then mine in any case where there is only one allowed input, because I peek first so I call twice in all cases. Theres gotta be a better way to do this
Where is that implementation from?
GitHub
SF_Mod_RefinedPower/Source/RefinedPower/Private/ModularPower/RPMPBu...
Contribute to mrhid6/SF_Mod_RefinedPower development by creating an account on GitHub.
Okay, that's ancient code
I said that a long time ago. Either way, its one of the two best examples I've seen. Nogs, shown above... also handles this by copying stacks.
Think I may have found a path forward tho... give me a bit
I can avoid coping memory entirely... if I implement my own Component->Inventory->HasEnoughSpaceForItem() I have... no clue if this is a better approach tho
Does this work? I'm not sure, but it seems you're getting the inventory of whatever is on the other end of the belt?
I think that connection would be the belt. Every belt has an input/output UFGFactoryConnectionComponent as I understand it. Similarly our Factory has a UFGFactoryConnectionComponent. So when we call Factory_GrabOutput on out Factories UFGFactoryComponent, I think that it is grabbing from the output of the connected UFGFactoryConnectionComponent (The Belt). I don't think you can get the Input Component from the Belt, just the Output. Unfortunately. Otherwise, we could navigate belt networks like linked lists š Which in my opinion would be very VERY cool.
Oh lol... you totally can... because you can get the outer buildable, you can walk from your connection component up into the buildable, and then down into an input connection component and into another output connection, up into the buildable... Wow... pretty sure once you've mapped an entire network you could do some crazy optimization somehow. Or add some crazy features.
It's not a good idea because of threading
Sorry whats not a good idea?
Oh to map the whole network and act on it? Yea Im sure there would be some hickups... but Im trying to create the best grab function I can right now
Any thoughts on what to do if both the input and the output are unfiltered inventories? Am I stuck peeking in that case?
From a factory buildable's
Factory_Tick
, accessing the inventory of another buildable (that is not a conveyor) is not thread-safeAnd here is where I run up against the limits of my understanding. Having my MS in Computer Engineering and the bulk of my work much lower in the stack... The highest level thing I've ever written was pieces of OpenShift to implement OpenShift on PowerVS and PowerVC š I totally understand that the Factory Ticks are executed in parallel. Hence the caution. But in theory... as Satisfactory owns all of the memory for all of its threads... couldn't I "safeishly" Read Thread B's memory From Thread A, while Thread B acts on it? Or does that just count as an access violation and seg fault? If it doesn't seg fault... if I'm reading memory that is being written, I'd guess we'd just get garbage back. But as I am just traversing the linked list... We "shouldn't" be modifying the connection data often. Similarly... I think, if we did get garbage back... that it might be possible to validate if we had received valid data or not. Wonder why the conveyor is exempt from this issue. I'd have thought it would follow the same rules as everything else.
Software Engineering Stack Exchange
Is a 1:* write:read thread system safe?
Theoretically, thread-safe code should fix race conditions. Race conditions, as I understand it, occur because two threads attempt to write to the same location at the same time.
However, what abo...