Oldskooler Ramblings

the unlikely child born of the home computer wars

Internal conflicts

Posted by Trixter on March 15, 2008


My work-in-progress tracker is my first object-oriented program. This has obvious advantages, such as making a fundamental change in the “abstract” portion of a chunk of the program, and having that change “inherited” by all of that chunk’s “descendants”. For example, I created a “screen” object with descendants for CGA, EGA, monochrome, VGA, and VESA. There was a bug in the cursor handling. I found it (turned out to be a bug in the ROM BIOS, if you can believe that), fixed it, and POOF all of the EGA/VGA/etc. objects now had the fix. From one change. Programming like this is somewhat intoxicating and it results in extremely readable and portable code.

On the other hand, OOP programming results in slower code. There are many C++ and Lisp/Smalltalk programmers out there who will defend to their grave this is not so, but they aren’t coding on a 5MHz machine like I am. (Yes, I am truly programming ON THE HARDWARE ITSELF, not cheating and using a fast PC to develop.) On such a slow machine, I can actually compare different techniques and can notice — sometimes visibly to the naked eye! — how OOP results in slower code than non-OOP. This is mostly due to the fact that nothing in an OOP program is “static” — it’s dynamic, which is best illustrated thusly:

Static program: I want data. I know exactly where the data is. I load the data. I process the data. I put the data back exactly where I found it.

Dynamic/OOP program: I want data. I don’t know where the data is, but I know someone who does. I ask them. They point me to another location. I go to that location. That location has a note saying “the data doesn’t live here any more; you can find them at their new location X.” I go to location X. There is my data! I load the data. I then want to process the data, but my engine was removed and replaced with a note saying “your engine was relocated over at Y.” I go to Y to pick up my engine. I fire up the engine and process my data. I travel back to X to put back my data.

Essentially, pointers no longer point to data or code, they point to other pointers. On fast machines, this double- and triple-pointer loading is no big deal. On an old slow machine where one byte takes 4 cycles to access (as opposed to today’s machines where EIGHT bytes can be accessed in ONE cycle, at clock cycles 800 TIMES faster than me), it adds up quickly. Real world example: My first attempt at repainting the pattern display was asking the song object for every single note in the pattern, then painting each one as I got it. It took two seconds to paint the display. So my optimization was to ask the song object to GIVE ME ONE ROW OF SONG DATA RIGHT NOW, DAMMIT and I process the entire row at once and paint the row. Now it repaints in about 1/10th of a second, which is doable for now. I am angering the OOP gods by doing this. I can hear their displeasure every time it rains.

The obvious tradeoff is flexibility. Let’s put my craptastic illustration skills to work again, this time explaining why OOP is a good thing: Let’s say I want to make something to eat. I have a recipe. I follow the recipe. I get a chocolate cake. I then wish to make apple pie. I change the word “chocolate” to “apple” in my recipe, then “cake” to “pie”. Here is what happens:

Static program: I get a chunk of food matter made of solid apples shaped like a cake and covered in chocolate frosting.

Dynamic/OOP program: I get an apple pie.

So where’s the internal conflict? My project is to create a music program that must be both flexible AND fast. Leaning toward the latter can completely screw up the advantages of the former.

My goal is to have actual music output before I go to bed tonight. Local time is 9:30pm. Better figure it out soon.

10 Responses to “Internal conflicts”

  1. Do you ever get the feeling that something is not quite right with the Object World? This site will help you unplug from the matrix that is OOP: OOP Oversold @ http://www.geocities.com/tablizer/oopbad.htm

  2. Trixter said

    I can’t tell if you’re serious or sarcastic :-) Reading the link, it appears the guy is pushing “table-driving programming” which means everything is some giant jump table.

  3. Oh, I’m deadly serious in my detestation of OOP. I’m a big fan of that OOP site. To be honest, I have never really figured out what that TOP stuff is that he likes. But if it works for him, great; OOP obviously doesn’t.

    It’s hard to imagine hard-core, real-time OOP work on a platform like the original 8088. More power to you if you can get it to work.

    Here’s a great war story about trying to make C++/OOP fit for an embedded platform (which is similar in spirit to what you’re trying to do): http://www.embedded.com/1999/9908/9908feat1.htm

  4. BTW, Google for the famous programming language comparison lists that describe how to shoot yourself in the foot using various languages. Your description of C vs. C++ is a perfect, practical illustration of what the lists state:

    C: You shoot yourself in the foot.

    C++: You accidentally create a dozen instances of yourself and shoot them all in the foot. Providing emergency medical care is impossible since you can’t tell which are bitwise copies and which are just pointing at others and saying, “That’s me over there.”

  5. Trixter said

    “It’s hard to imagine hard-core, real-time OOP work on a platform like the original 8088. More power to you if you can get it to work.”

    I’m flattered. It has definitely resulted in more up-front design work than coding. I have spent up to 10 minutes simply sitting and thinking at the keyboard instead of typing.

    While I am definitely taking some shortcuts where speed is concerned, it is mostly OOP. I had to not use OOP for the interrupt handler, but so far so good. The most significant “cheat” I am using is TSong^.GetCurRowData which returns a pointer to the current row of data (used by both the screen repaint and player engine). This is a “no-no” in OOP but I need speed, dammit :)

  6. Trixter said

    BTW the “TOP” stuff the guy refers to is simply a jumptable — from what I can understand, he’s emulating OOP’s virtual method table but without the constraints of OOP. That’s not necessarily a good thing.

    OOP isn’t evil; it’s just not the right choice for a lot of stuff. In hindsight, I probably shouldn’t have started MONOTONE as an OOP program. But programming is programming, and it’s all fun.

  7. Jay said

    I’ll just throw my 0.02p in here if I may.

    (1) Everything costs something. The flexibility and code reuse that OOP encourages is usually paid for by a reduction in speed. However, this speed reduction can often be insignificant in the places where it actually matters.

    (2) Turbo Pascal and many other OOP languages allow “static” object methods as well as virtual ones. There is essentially no performance hit over and above procedural programming when calling these.

    (3) The x86 has always had very poor performance when it comes to loading segment registers, so OOP (or any heap-centric design) has a significantly larger overhead for programs that use the “Large” memory model, like Turbo Pascal. Blame Intel for this, not OOP. Move to a 32-bit “Flat” memory model and the pointer load overhead is greatly reduced.

    (4) I think Trixter is completely insane in wanting to write anything for the original IBM PC :-) It was pants by comparison to many of its pier machines even way back then!

    (5) Trixter is right in that using a jump table is essentially emulating what OOP does (or visa-versa). Both have their uses,. but OOP is generally preferred because it’s much less of a PITA and adds a lot more (such as encapsulation) that jump tables do not.

  8. Trixter said

    replies:

    1: Agreed, and as I’ve written before I have created workarounds where necessary to handle the speed issue.

    2: You know, I never thought about static methods… this would burn up a lot of memory in the global data segment, but it would indeed be much faster. Hm…. Let’s see if I can fit into 64KB; if so, I might convert over. If not, it’s dynamic methods for me!

    4: Guilty as charged :) but that’s what makes it fun.

  9. Jay said

    Re:(2)

    Static methods won’t use any space in the global DS.

  10. Trixter said

    But they’ll take up additional code space, due to their nature of being static instead of dynamic. They’ll be out of the global data segment, sure, but the objects are larger as a result. Tradeoffs…

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: