Dirty Coding Tricks
gamasutra.com
gamasutra.com
From a customer's perspective, you are always allowed to make the software faster with a future patch but never slower. The issue is that sometimes you find bugs (or add/complete features) where the correct implementation is somewhat slower than what you already shipped or planned for. In these cases, you end up creating two patches: one to address the customer issue and one to add an optimization to return the overall system performance to its prior level.
I've shipped complex v1.0 software where low-hanging optimizations would probably have made the system 2-5x faster (though still perceived as being extremely fast). Over time, it allowed us to add in expensive features and bug fixes without perceptible reductions in performance by harvesting the optimizations we'd banked in the first version.
In 1995 I worked in a Dutch research institute where the file server was always almost out of disk space. Whenever some space freed up, my manager used to create something he called a 'balloon': a directory with 20 files of 5 MB each (which was huge back then).
If the disk was full and we needed space, he just removed one of those files and we could quickly write our files.
Hoarding in a situation of some scarcity is common behavior, and makes the problem worse. You can't assume people will behave the same way when a resource's availability changes. And this is why we go from plentiful to very scarce so very quickly, with very little in between
you become VERY creative when hard times hit :-)
The default is (usually) 5%. In these more bountiful days of multi-terabyte disks I generally knock that down to 1%.
Which reminds me of being a kid and mom telling me to turn down the tv - I would "accidentally" hit the wrong button and turn it way up for a few minutes, then just turn it back down to where it was.
An engineer of elevator systems would spend hours doing all his calculations by hand, vowing it was too complicated and important to trust to a computer. (Yeah.)
The programmers in the company assured him they could write a program to do the calculations for him, which they did. He never trusted the results though, insisting that the program was not producing correct results.
One of the programmers realised that he didn't trust it because the program spat out the results almost instantly. It was too fast, it had to be wrong.
So he stuck a twenty second sleep in before the output, told the engineer he found the problem and fixed it. The engineer now felt the results were correct.
(Aside: I don't think I would want to get in a lift designed by an engineer who intuits the correctness rather than verifying the math, but I thought it was an interesting little parable on user interface design.)
The entire concept of "we saved 10 million hours of productivity by shaving 1.8 seconds off this process" is bunk. You saved 1.8 seconds of productivity n million times, which adds up to not a whole lot of extra productivity.
Put another way: Whether $APP takes 0.1 seconds or 1.9 seconds to do something does not at all change what time I'm leaving at the end of the day.
[0] http://googleresearch.blogspot.com/2009/06/speed-matters.htm... [1] http://www.webperformancematters.com/journal/2007/7/10/accep...
But it does greatly change how annoyed you leave at the end of day.
Not to mention any noticeable delay in load times will quickly get people to start multitasking, which can easily change a 2s delay into a 2min delay. This can easily ... well it does make a difference.
I noticed this mostly while testing code. After clicking through the interface many times it can easily add up to an hour less actual work getting done that day.
Every time you buy stuff on sale at the grocery store, you're seeing it in action, but you probably don't think much about it.
For example as an engine programmer you'd want to pre-allocate space for various resources (models, textures, etc.); some stuff is dynamic, like the currently active game entities. So you'd have preallocated chunks of memory for N entities, where N is the maximum number of entities you could handle at that time (at every opportunity you'd want to avoid classic memory allocators because they're slow and a fragmented heap could kill your game on a limited memory device; so everything would be done with fixed block-size, fixed length, linked-lists). It was relatively easy to 'hide' buffer space for a rainy day because the list sizes would just be magic numbers that were decided as 'roughly what we'll need for later'.
They could be per level magic numbers, or system wide magic numbers. Nobody gives a shit about the magic numbers at the start of the project on development devices with more memory than the final device, and mostly nobody even knows what the game's going to be early on (this was something I saw a lot in the games industry: teams just feeling in the dark for what it is they're supposed to be doing for months on end).
Towards the end of a project when things are tight, and you have a better sense of how much of each type of thing you'll need, that's when you start giving up the buffer.
Reading TFA reminded me of how much I do not miss working in the games industry. It was fun while it lasted (I did it for 10 years, I left 10 years ago), but after a while this style of programming drags you down. It's too much stress, for mostly not enough reward.
The last game I worked on: https://www.youtube.com/watch?v=G9zMWu6PfSM
The CPU equivalent would be the "speed up loop" that somehow avoids being recognized by a profiler.
Sorry, but this guy sounds like an asshole. Derailing a project so you can be the one to save it is pretty sleazy. What about the stress on his colleagues, the late nights, etc? They were "frantic" and "exhausted", and he was sniggering the whole time. The human cost of his moment of glory?
Taking credit for the last 2MB is a dick move for sure though.
Think what would have happened if he hadn't added the alloc. The team would have been in the same situation, would have busted their guts, got it down under budget and slapped themselves on the back. They might have got it down earlier too: who knows how many person hours got wasted on the last 384K of savings that were never needed.
Think what could have happened with the alloc: the team is hovering around 3-4MB above budget and running out of ideas, with major efforts shedding only a few tens of KB each. They agree they might manage to cut 1-2Mb with a herculean effort, but 4? No way. So with the deadlines approaching decisions are made to cut some feature or content to avoid failing TRCs or missing contracted delivery dates.
If you have to get it down under target or cut something else, then I can't think of a single situation in which having an artificially low goal will help you.
At the very least, the fact that the team thought this guy somehow helped them ship rather than adding to their problems and lauding it over the actual engineers who'd bust their ass to make the savings, is a huge jerk move.
This itself implies that the guy WAS one of the "actual engineers" busting their asses.
Now, whether or not you actively hide a budget padding is another thing. That is up to the seniors of the project.
As a project manager you are also supposed to pad your project schedule a bit without showing your client or engineers the padding.
One of them noticed that the projector screen that the demos would be shown on had a vertical splice. He eyeballed the location and tweaked the parameters to make the artifact line up with it.
No one noticed.
In the end, I just rewrote the score checker to verify the top of the game, instead of the bottom, and delivered a game where pieces felt down. Everybody not just noticed, but I had a line of people wanting to play it.
Unlike many other types of reusable code, this sort of gameplay hack will be thrown away and won't be used again in a subsequent game. To an extent it justifies its use in a pinch. But no craftsman is ever happy shipping something like it.
It has been my experience that at the end of game development projects many of these sorts of hacks start flying. Most of them are unnoticed by players.
No game is ever perfect. With limited resources and time it's important to keep in mind what players care about and focus on those things. I care deeply about providing a good experience to players and one of the most interesting and challenging components of game development is deciding where to apply resources when under tight constraints to deliver the best experience. I haven't worked outside the game industry in a long time but I'm sure this is true in most consumer software businesses.
Back on Wing Commander 1 we were getting an exception from our EMM386 memory manager when we exited the game. We'd clear the screen and a single line would print out, something like "EMM386 Memory manager error. Blah blah blah." We had to ship ASAP. So I hex edited the error in the memory manager itself to read "Thank you for playing Wing Commander."
(This will be understood only by those who lost countless hours tweaking their autoexec.bat and config.sys to squeeze that last KB of memory needed to run the game they were supposed to spend the weekend playing ).
I must misunderstand something because the hack is going to fail too, except that you are going to end up with some controller id in your allocator's free list or some other such nonsense...
That last one is best. After having to struggle and fight for 640k for /the heart of a renderer/ if someone had left 2MB lying around that would have been great. Then again so would not using loads of memory for all sorts of weird and wonderful things that are much less important... like C struct padding, or making everything into memory pools, before you have any memory problems. :)
Edit: You probably shouldn't do this in new code, but in the context of memory-constrained C code it's a nice hack.
https://www.mikeash.com/pyblog/friday-qa-2013-09-27-arm64-an...
If I understood correctly, the pointer is just a field in a common struct (common in event-handling code). As he mentions, it's usually used to point to extra data that you allocated separately in case you need it for that specific type of event, but what if you're having trouble with allocation and your data conveniently fits in the pointer field itself? So he just cast his controller id into the unused pointer parameter. He's not mangling any allocator data.
Which, if you're not going to use that pointer parameter otherwise, I think is perfectly sensible (and as long as you don't have code along the line of if (ptr != NULL) free(ptr);). I do it all the time... cough. For example, when creating a thread, you can pass it a void* to some data for it. I just cast a threadID integer into there. Hell, you'll see just that in the first example on a google search for "pthread example"!
hopefully the controller id was just the lowest bit or two, which would then align back to zero or something when it came time to free ;-)
Assuming the controller ID would normally point to somewhere in protected memory, the frees would have no effect, and he could store the controller ID in the pointer parameter as the address of the pointer.
maybe they used a custom allocator though... its very common.
I really liked the final problem's solution!
Anyways, just rambling a little :-).
Spurred on by the success of "if A==bad then NOT A", I used this tool to patch several more bugs -- which nearly all had to do with the collision code.
This reminds me so much of a developer I work with, why figure out the root cause when you can throw a try / except / pass around it. If that doesn't work upgrade a package and see if that helps. If that doesn't work throw in a hacky fix.They're also the first to blame "GCC/Python/JVM/etc" and saying there's a bug there.
Sometimes the rubber hits the road and you don't have time or money or any more cognitive energy to understand why your code that calculates a lot of things from a lot of data is not working in that 0.01% of cases
So you come up with a quick fix that's not exactly what it has to be done but it works it ships and it pays the bills.
Of course I don't advocate such solutions on critical paths, ate least not without some double checking.
1 - People "avoiding technical debt" by overengineering.
2 - (As you mentioned) sometimes it's not even your fault that you need a hack. (It may not even be a bug on what you're using, but just an unfortunate impedance mismatch, like the Playstation problem mentioned)
After all, what gets burned on a PS1 CD or PS2 DVD is permanent.
I'm sure that's at least somewhat different today with so many games having scheduled DLC and other updates that make the technical debt more unpalatable, although I'm sure such things still go on at a high rate.
[1] http://www.codeofhonor.com/blog/tough-times-on-the-road-to-s...
[2] http://www.codeofhonor.com/blog/the-starcraft-path-finding-h...
[0] http://www.dodgycoder.net/2012/02/coding-tricks-of-game-deve...
The print version is so much more readable with a wider body and no comment clutter!
However, if they reuse filenames, then the chance of collision is much higher. For example, assuming 1000 distinct filenames chosen at random, the chance of collision rises to about 1 in 10,000.