Resource management

Started by
29 comments, last by SmkViper 9 years, 5 months ago

Knowing things by policy is awesome for speedups. Unfortunately I (as a human) am constantly breaking my own policies on accident and those bugs are always a major pain to track down...


can't do that. coder discipline is mandatory in code that uses behavioral policies. without perfect coder discipline, you can shoot yourself in the foot. you might think of policies as allocated objects you need to remember to deallocate - IE they're around and you have to keep them in mind all the time and handle them properly to avoid problems. another one of those dangling string type things you have to be sure keep in mind and to take care of. In the case of policies, its more about remembering they are in effect, and not violating them, rather than remembering they are in existence and must be dealt with.


I still claim people are misunderstanding me.

It's not a matter of discipline. No amount of discipline will prevent me from typing "teh" instead of "the" sometimes. We're all human, we all make mistakes - from typos to logic errors. If discipline fixed all that then no one would ever need to debug anything.

I'm simply advocating that, whenever possible, use the features of the language to enforce your policies in a way that you can catch violations at compile time. Or at least at runtime. Sometimes that's not possible, but for a large majority of things it is.

In the case of threading, locks enforce policies at runtime. If you find out they're slowing you down, then figure out ways to remove them (i.e. redesign) But don't waste a lot of time making your code harder to read and maintain in areas where your profiling tools are showing you that performance is not an issue. (And lockless code is harder to maintain because it doesn't protect you from mistakes)
Advertisement

I still claim people are misunderstanding me.

It's not a matter of discipline. No amount of discipline will prevent me from typing "teh" instead of "the" sometimes. We're all human, we all make mistakes - from typos to logic errors. If discipline fixed all that then no one would ever need to debug anything.

I'm simply advocating that, whenever possible, use the features of the language to enforce your policies in a way that you can catch violations at compile time. Or at least at runtime. Sometimes that's not possible, but for a large majority of things it is.

In the case of threading, locks enforce policies at runtime. If you find out they're slowing you down, then figure out ways to remove them (i.e. redesign) But don't waste a lot of time making your code harder to read and maintain in areas where your profiling tools are showing you that performance is not an issue. (And lockless code is harder to maintain because it doesn't protect you from mistakes)

I would argue the exact opposite of this.

This is not "for beginners". Beginners do need systems that strictly enforce the rules. Beginners need safety scissors. Beginners need special pumpkin-carving knives rather than traditional paring knives.

Game engine development is not a place for beginners. It is a place for system architects. While somewhat abrasive, I completely understand why Linus Torvalds chose to utterly reject c++ code from kernel development: the abstractions and safety nets are not free and in systems-level programming the cost of using them is sometimes unacceptably high. By rejecting C++ code from the kernel he also rejects an enormous volume of developers whose skills and understanding are less than stellar. Yes, it does introduce a cost to development. But the benefit is source code that strictly follows certain policies. Discipline and policy adherence, not tools that hold your hand and wipe your bum, are critical.

As one of many examples, classic memory managers and classic destructors provide an excellent safety net. Unfortunately they also present a terrible burden in some places, which is why many game development libraries provide an additional "throw all the memory away" option that avoids destructors and proper teardown. You need to follow policy correctly, you only use it when policy says it is safe because it is a potentially fatal function. Is it unsafe? Absolutely! It is a scalpel to be used with prudence and care. Similar functionality was partially added to C++11 because it is so prevalent in many industries, but in the hopes of giving a safe tool many people find the tool inadequate for their needs. Because they ultimately chose 'safety' they made a tool that is unusable.

This is not a topic about beginners or junk code. This is a question about resource management inside an engine. That is system level work.

Pulling back toward topic:

Resource management.

You need to manage when resources come alive. You need to manage how they move through their life cycle from pre-load to loading to loaded to active to archived to discardable to unloaded. You need to manage when resources are destroyed.

ALL of those steps are policy based.

If you don't care about policy, any system could load data from any arbitrary data source and feed it directly to the graphics card. This situation does not scale well and is unlikely to perform well.

But we do care about policy. We follow policy strictly. We build systems around policy, such as building object pools, object loaders, and proxies/handles that can help you coordinate with objects as they move through their life cycles.

You write it is not a matter of discipline. I say it is ENTIRELY a matter of discipline and policy. You must have the discipline to use the tools provided, to use the model loader and the model cache rather than calling fread() to open your models. You must have the discipline to use your a carefully-designed lock manager policy, that you must always call AcquireLocks() and ReleaseLocks() every single time you work with locking objects. You must have the discipline that every time you allocate memory you ensure that you release the memory or transfer ownership. You must have the discipline that every time you modify the source code to remove some functionality you also review the entire code base searching for comments about it and references to it and systems that use it indirectly and modify those as well. You must have the discipline to run the automated tests before submitting the code even if it takes 10 minutes. You must have the discipline to always run and test the code in game, even if it takes several hours to test all the facets before submitting the code. All of these are absolutely a matter of discipline and policy.

You write that you should let the compiler do it so you don't have to. I agree that you should lean on the compiler when appropriate, you can make compile-time checks for a lot of features. You can build a tool with a list of functions that ensures you have acquired and released locks inside the function. You can lean on the compiler to ensure all references have been renamed or removed, you can lean on the compiler to ensure syntax is correct, you can lean on the compiler to do a lot of optimization, but you cannot blindly assume the compiler will be your parachute. It is quite common for the compiler to miss things. But ultimately, the compiler must presume unless the programmer directly contradicts the language rules then the programmer is always right, only the programmer is responsible. You can tell the compiler to do something nonsensical and it will attempt to do it. It might make optimizations that eliminate dead code, or improvements to lift things out of loops or other sloppy programming, but beyond that the compiler will assume you are right. It is not the compiler, but the programmer and their discipline that ensures it is correct.

Sure as part of your careful, rigorously-designed policy you can create an object that obtains a lock and does so in a well-designed system. You can then write policy that tells programmers they must create an instance of the object, and policy that says what the object does, and policy that says what not to do. You can even write tools that help enforce the policy by validating the source code against custom-built tools.

But ultimately if the programmers violate the policy, either as a sin of commotion or a sin of omission, that is a failing of the programmer. Discipline and policy are critical.

Object lifetimes and resource management is first and foremost a matter of policy. A policy of when objects are created, and what is allowed to create them. (And policies for what is forbidden from creating objects.) A set of policies for what systems own the objects and how they are treated in a wide variety of situations including exceptions. (And policies for how they are not to be handled, how they are not to be stored.) And policies for how objects are retired, interred, and destroyed. (And policies for how they are not to be destroyed.) The code and classes and tools merely implement the policies.

I guess we're going to have to agree to disagree on this then - which is fine.

I agree with some of what you've said, but you seem to have some giant expectation for "professionals" to be perfect and not to make mistakes.

There's a time and a place for low-level code without safety nets. But that should be carefully controlled, heavily tested (preferably automated) and only where your profiling proves it's necessary.

Your compiler is a tool. Use it. Let it help you where it can.

This topic is closed to new replies.

Advertisement