I can't help but feel I'm fighting a losing battle here with these GC spikes and, probably worse, GC per frame. My base garbage collection is currently around 12-13KB and I believe that almost all of it is coming from ORK, which isn't surprising given the size of my project--1400+ abilities, 300+ Status Values, 1200+ formulas, 100+ equipment slots, dozens of variables per item with multiple update variables apiece etc.--but there must be something I can do to get my base framerate down to a more manageable level so that I'm not always fighting stutter issues when I run large events. So, with that in mind, a few questions:

-Do unconnected nodes left hanging around inside formulas add to garbage collection, or adversely affect performance in any way? I noticed they seem to effect events, probably just when they're initially loaded.

-Does the total physical space as conveyed by the GUI inside of ORK's events and formulas impact performance at all? I like to build these with a lot of space for adding or making changes down the road, and thought that perhaps I could improve performance a little by using the handy vertical/horizontal sorting feature included in ORK. I doubt this feature is actually represented in the compiled code, but it's been hanging around in the back of my mind so I figured I'd ask.

-How big of an effect to Combined Status Values and Update Variables have overall?

-Will multiple individual HUD's impact performance more than would fewer combined HUD's with the same amount of features?

-Does the overall size of of the ORK framework itself impact garbage collection? It certainly extends the lag whenever I try to move anything up or down on a list, but that's to be expected as I'm sure it has to update all of the dependencies.
  • None of the ORK settings should cause GC by just being there (same goes for e.g. not connected nodes). GC collects garbage, i.e. stuff that's no longer used anywhere - all of ORK's settings are used and referenced and are thus not affecting that.

    What will cause GC is stuff that's only used short term - e.g. if your UI constantly updates, that'll cause garbage. ORK 2.28.0 greatly improved (or rather, lowered) the HUD update rate to only update when something actually dispalyed changed, so there should be less garbage coming from that, but it depends on the use case.
    E.g. if a HUD is constantly updated due to showing a running time (like status effect time) that'll add to the GC.

    Constant use of formulas will also cause garbage, while the formula itself doesn't affect it, the call and calculation will produce some garbage. Petty much everything in a running game will produce some garbage, as there things only used short term (e.g. a variable within a function call) that need to be cleaned up.

    As for your questions:

    - unconnected nodes don't cause garbage, as said, settings are loaded and kept around, so there's no garbage from them.

    - physical space between nodes also don't cause garbage, especially in-game, the physical position has no influence on anything :)

    - they'll produce garbage due to their calculation, it shouldn't really have an impact, but as with most things, it depends on the use case. E.g. 1 combined value that's updated from time to time is no problem, 100 that are updated each frame might be ...

    - depends on the use case, if one huge HUD needs to be udpated each frame due to displaying so much information that's constantly changing, it'll lead to more GC than having multiple smaller HUDs, where each is updated at different times and only a small portion is updated constantly.

    - by itself, ORK's project size doesn't impact it (as said, the data is kept loaded and isn't garbage collected), however, a larger project usually means more things are going on, so that'll impact GC.
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • So, it sounds like the combined stats might be a major culprit. 13kb average, though, for around 50 combined stats? Do larger formulas cause significantly more garbage? What about update variables, are they treated pretty much the same?
  • 1400 abilities? You are not kidding around :D !

  • It always depends on what's being used within - a simple formula call doesn't produce that much garbage, but if you're using local variables within, that'll increase the garbage.
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • @hellwalker - The majority are tied to skill categories and offer passive bonuses. They're what most games would probably call perks that you can purchase to flush out a character, or simply ignore for a passive skill XP bonus as they accrue. In any event, given what GIL has said they're probably not the root of my garbage collection problem, although I suspect they're a big part of the reason it takes around thirty seconds to move things like status values around.

    @gamingislove - My items go a little crazy with local variables. Actually, if I'm being honest, I've gone pretty much full on bat guano off-the-rails with local item update variables. I've gone so fare as to write most of the item descriptions using string variables that change based on an item's properties, condition etc. That said, I'm seeing these garbage collection spikes in the main menu, before the game has even started. What could be causing that?
  • Well, item/equipment variables are kept alive on the item/equipment instance anyway, i.e. as long as the formulas don't transfer stuff into local variables (which are created per formula call, i.e. create garbage) it wouldn't have an impact on that.

    Have you tried using asset bundles? ORK's asset source system allows you to quickly change direct asset references to asset bundles in the ORK project, but you'll still have to first set them up in Unity.
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • "Well, item/equipment variables are kept alive on the item/equipment instance anyway, i.e. as long as the formulas don't transfer stuff into local variables (which are created per formula call, i.e. create garbage) it wouldn't have an impact on that."

    -That's precisely what several of them do. Would it have less of an impact for me to create a host of new selected variables tied to the item instead?

    "Have you tried using asset bundles? ORK's asset source system allows you to quickly change direct asset references to asset bundles in the ORK project, but you'll still have to first set them up in Unity."

    -I have not, but I'll look into how that's done and thank you for the tip. Incidentally, I tried running my project after switching all of my stats from combined to regular and removing items from every scene combatant. It reduced the garbage per frame substantially, but the CPU profiler still looks about the same; I'm running baseline around 30fps with constant interval spikes over 30. Most of my draw seems to be Vsync, which I'm sure is important for high paced action games and the like, but seems unnecessary for something like a top-down party based RPG with active-time/turn-based combat. What's the worst that could happen if I just turn that feature off? I doubt it would have any effect on ORK.
  • Yeah, having the variables on the items would be better. Generally I'd recommend to not doing mass formula calculations each frame, unless it's absolutely needed.
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • Just how much garbage does an update variable with local variables generate per frame? Let me give an example of what I've done, showing just how much of a cluster-#&$^ it will be to redo.

    -So, firstly, I've created a central block of formulas representing the kinds of physical materials that items can be made from. The materials are grouped into formulas for items made of metal, minerals, wood, bone/horn, cloth/hide. The formulas each contain a catalogue of materials running down a list, such as iron, steel, copper, bronze etc., for metals, and each check node dumps into a value. there are material groupings such as these for every type of variable that needs to look for a numerical value to indicate a specific material property, like hardness, or toughness or weight (atomic density).

    So, basically, I have a list of 5 formulas holding the base hardness values for item materials, 5 for toughness, 5 representing a base weight multiplier and so on. However, that said, it's not as simple as being able to cover every item (or part of an item) referencing a single 'selected variable', because multiple variables need to reference these values. Thus, to keep everything tidy and ensure that I could plug as many variables as needed into so many pre-defined property values, I used local variables rather than selected.

    -To make this process clearer, I'll use a piece of armor as an example. All of my armor, whether it uses them or not, is set up to reference two layers of material, namely a basic functional layer and a substrate. A plate cuirass holds variables for both its outer plate material and a substrate, but only the former is assigned any actual values whereas a brigandine, which consists of floating plates bound to a jacket, needs both regular and substrate material variables. In any case, there are basically four variables that act as a key to determining how the rest are going to branch out when searching for a specific material value, and those four are: 'Amr_Material', 'Amr_Material_Type', and 'Amr_Material_Substrate', 'Amr_Material_Substrate_Type'.

    All other material-related variables attached to an item are tied to their own formulas which, at some point, will link to a formula called either 'Amr_MaterialMOD_Total,' or 'Amr_MaterialMOD_Substrate_Total.' Those formulas will check either the selected variable 'Amr_Material_Type', or 'Amr_Material_Substrate_Type', and depending on what this string value indicates will link to a corresponding formula that will run down a list checking for a specific material (iron, copper, steel), and changes it to a local variable. Finally, the formula checks its current numerical value, which will have been assigned as an initial value by the variable or formula that originally called it. Depending on this assigned number, the formula will then branch to an appropriate material properties forumula. A value of '0' in the 'metals' formula referencing 'copper' would instruct the formula to look under 'Materials_Hardness_Metals' for the value indicating the hardness of copper; '0.1' would check for the value representing copper's hardenability; '1' looks for the general toughness of copper, and so on.

    I'm sure that's all confusing as hell, but the point is that it seems to work beautifully, apart from the garbage collection issue. A steel plate cuirass of given proportions can weigh the same as it actually should, and if crafted to different proportions adjusts accordingly to weigh, again, as it actually should. If a combatant is wearing a steel cuirass over a padded aketon, and another combatant stabs him in the chest with a pollaxe, each layer of the material resists according to its physical properties until the force of the blow is either expended, or the material layer is breached, in which case the next layer of material will resist based on its defined physical properties vs. those of the weapon coupled with the remaining force. None of this would be possible without the information assigned by formulas to a series of interconnected variables that update after changes are made.

    Let's say that, in all, maybe 80 variables use local variable swap formulas, and out of those maybe 15-20 are update variables. How much garbage collection is really represented by swapping to local variables, and might the complexity of the formula chains themselves represent a bigger issue? I can see doing away with update variables and instead opting to destroy and re-spawn an item every time an event changes one variable that effects another--that would be a tremendous pain in the ass, but doable--but I don't know if it will be possible to do away with local variables inside those item formulas without scrapping most of my setup.

  • It's really hard to tell how much memory is used by a formula call, as each node itself also has things going on that can cause garbage.

    The call itself (that keeps reference of the value, user/target, variables, etc.) is at least 20 byte, if local variables are used, that'll at least add another 80 byte (and increases with each variable and dramatically when using different types).

    Your biggest issue seems to be the constant use of them - is this really needed? Also, destroying and respawning stuff will create much more garbage than updating existing things :)
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • "The call itself (that keeps reference of the value, user/target, variables, etc.) is at least 20 byte, if local variables are used, that'll at least add another 80 byte (and increases with each variable and dramatically when using different types)."

    -So, when you say that a call is 20 bytes, do you mean the initial call from the stat/variable, or every call to another formula from within a formula? Do nodes themselves produce garbage when moving from one to the next? I take it that's also multiplied by the total number of combatants in a scene along with their inventory and all of their equipment, as well as any per-generated items placed as objects.

    -Is 20 bytes per call a static number, or simply a minimum that scales per the size and complexity of each formula called?

    -Are the 80 bytes from local variables generated per each one existing inside a formula that's called, or merely when the variable is activated by selecting its node?

    -I'm assuming that standard, non-update variables will only generate their garbage when an item is initialized, whereas Update Variables constantly generate the 20+ bytes. In other words, one is probably kept as a Struct and the other a Class. Is there a way to have them simply monitor for changes to certain variables? What about only calling the Update Variables under certain conditions, like when they're equipped or during combat?

    -Would it be more or less efficient to tie all of my update variables into a single formula, or more like six to one vs half-a-dozen to the other? Currently, all of my item variable lists end with a single variable called, '_MiscProperties', that basically does just that--it checks certain random variables that aren't constantly updated and makes changes to those they would effect.



    "Your biggest issue seems to be the constant use of them - is this really needed?"

    -That's what I'm currently weighing. The not so simple answer is, in some cases, yes, but I'll basically be triaging certain ones that the game can likely do without. An example of a feature that I can, and probably will, remove is the ability for heat related attacks to ruin the 'Tempering' of metal equipment, altering its 'Hardness' and 'Toughness' properties. Scrapping this feature will allow me to remove those particular variables from the update list and save some space.

    On the other hand, something like one particular Float Variable that I've named '_Oil', which is factored as part of the '_MiscProperties' formula chain and used to alter the resistance of metal items to 'Oxidation', is something I'd rather not do away with and which needs to be able to effect other variables in real time. On the other hand, I might be able to get away with simply relegating it to be checked as part of a formula that's called only when something attempts to oxidize a combatant's equipment, and perhaps also remove the related resistance variable from the item entirely. I guess that's how optimization works. If it weren't such a colossal pain in the ass, every game would likely run better.



    "Also, destroying and respawning stuff will create much more garbage than updating existing things :)"

    -Is there any way to update specific variables, along with those others related to them, only whenever they're changed by an event? There are plenty of variables that I need to keep track of, but would mostly remain static except in cases where an event changes them, and so I don't really need to keep track of them frame by frame :)
  • Every formula call produces at least the 20 byte, even within a formula - and many nodes will also produce additional garbage, as with many function calls in programming, that's just how stuff works :)

    Trying to micro-manage your formulas to prevent garbage collection isn't the solution to your problem, trying to reduce using the formulas each frame is the only way to improve things.
    I guess you're already using the Use Update Variables settings in your items/equipment/abilities? Since they already automatically update variables, you'd just need to reduce the constant change of variables on them. E.g. does a weapon's whatever value need to be udpated each frame, or is it enough to do it every second.

    The only other way I see is doing things via custom scripts, which should be easy to hook into ORK's systems, e.g. via the init game events for equipment and registering to variable upates.
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • "Every formula call produces at least the 20 byte, even within a formula - and many nodes will also produce additional garbage, as with many function calls in programming, that's just how stuff works :)"

    -So, I'm guessing that includes 'Value Nodes' that set the value to a formula, including the one that's currently active, and 'Check Value' formulas that do the same. What about simply checking for set values, or running basic calculations against them (multiplication/division)?

    "Trying to micro-manage your formulas to prevent garbage collection isn't the solution to your problem, trying to reduce using the formulas each frame is the only way to improve things."

    -So, regarding Combined Status Values, might it be more efficient in certain cases to monitor the other key Status Values that would effect them value and use the event system to update their values whenever there's a change?

    "I guess you're already using the Use Update Variables settings in your items/equipment/abilities? Since they already automatically update variables, you'd just need to reduce the constant change of variables on them. E.g. does a weapon's whatever value need to be udpated each frame, or is it enough to do it every second."

    -I would actually prefer most things, if not everything, update after every second or longer, rather than per frame, and collect the garbage incrementally. Actually, the best option would be to force most Variables and Status Values to update whenever there's a change to the key Global Variable, 'Time_Passed'. Other things, like Item Update Variables, would do better to wait for another of the attached item's variable to change, or when prompted by a formula/event. Will I need to write a custom script to achieve that?
  • You can assume that pretty much everything produces garbage, as that's just how stuff works - but it's usually just very small amounts, e.g. a float value has 4 bytes, a bool only 1, etc.
    What kills performance is the quantity of usage. A formula producing 200 byte of garbage once every second is 200 byte of garbage per second - 50x that formula each frame is 10kb garbage each frame.

    When do your combined status values need to be changed? Generally, this happens automatically when the combatant's stats are recalculated (e.g. when a status value changed). You can also do that via the event system using a Reset Combatant Status node.

    How often is Time_Passed changed and where is it changed? E.g. if this happens only via events, you could already do that (either directly in the event or via a global event).
    Please consider rating/reviewing my products on the Asset Store (hopefully positively), as that helps tremendously with getting found.
    If you're enjoying my products, updates and support, please consider supporting me on patreon.com!
  • "When do your combined status values need to be changed? Generally, this happens automatically when the combatant's stats are recalculated (e.g. when a status value changed). You can also do that via the event system using a Reset Combatant Status node."

    -Do combined stats normally calculate once per frame? I can't really think of any stat that would need to be recalculated until one of its underlying modifiers changes, so I suppose it might be best to use the event system to change all of them. The ability to monitor for changes to various stats before altering them through an event is certainly a handy new feature in that regard, but would it not be even more useful if we could simply bypass these events by using the same monitoring feature to determine when a Combined Status Value needs to recalculate? Loading an event that calls a formula certainly produces more garbage than just calling the formula directly.


    "How often is Time_Passed changed and where is it changed? E.g. if this happens only via events, you could already do that (either directly in the event or via a global event)."

    -My in-game clock/calendar event changes, checks and then re-calibrates this variable once per second. The game clock event adds one to the float value of, 'Time_Passed', every cycle before swapping it for another variable called, 'Date_Day_Minute_Seconds', and then calculates a change to the time of day. Whatever is the current float assigned to 'Time_Passed' (which can be altered by events intended to take up a certain amount of time, such as reading a book), determines how many seconds are added to the game calendar. So, for status values that may need to change in real time, such as when regenerating stamina, bleeding or gaining fatigue, it's essential to keep track of this particular variable each and every second before it reverts to zero, rather than simply updating once every second. To do this, I placed a 'Custom Combatant Node' in my clock/calendar event which effects the Player, Enemy and Ally groups by changing a single status value to equal the current float assigned to 'Time_Passed' prior to swapping it for the variable called, 'Date_Day_Minute_Seconds'. The stat is basically an internal counter that other status values monitor for changes and update accordingly based on its number until they're caught up. It's a little clunky, perhaps, and I haven't fully tested the system, but so far it appears to work.
Sign In or Register to comment.