Allegro.cc - Online Community

Allegro.cc Forums » Off-Topic Ordeals » Programming Paradigms and Useful Features

This thread is locked; no one can reply to it. rss feed Print
 1   2   3   4 
Programming Paradigms and Useful Features
Chris Katko
Member #1,881
January 2002
avatar

What methods, paradigms, langauge features have you seen that you find either useful, or wish the general public knew about?

For example, as I mentioned in a de-railed thread, Verilog (and apparently Objective-C) supports "Named Parameters" or "Pass by Name" (though the second term can apply to something else as well).

[window addNewControlWithTitle:@"Title"
                     xPosition:20
                     yPosition:50
                         width:100
                        height:50
                    drawingNow:TheDrawingVariable];

Notice how we can have optional parameters (and not just on the end!), the order is whatever we choose, and the intent of the call is clear and concise.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Thomas Fjellstrom
Member #476
June 2000
avatar

I'm pretty sure ObjC's arguments are not re-orderable. the actual name of the symbol is baked into the argument's order.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Slartibartfast
Member #8,789
June 2007
avatar

Python's are reorderable.

def foo(a=4, b=10):
    print a*b

foo()
> 40

foo(b=2, a=2)
> 4

But also

def bar(**kwdargs):
    for arg in kwdargs:
        print arg, kwdargs[arg]

bar(a=9, b="cool")
>>> a 9
 b cool

Chris Katko
Member #1,881
January 2002
avatar

I'm pretty sure ObjC's arguments are not re-orderable.

Noted, I'm not actually familiar with Objective-C. Only Verilog.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Steve Terry
Member #1,989
March 2002
avatar

not the first time I've seen named parameters, I believe OS/390 even had them.

___________________________________
[ Facebook ]
Microsoft is not the Borg collective. The Borg collective has got proper networking. - planetspace.de
Bill Gates is in fact Shawn Hargreaves' ßî+çh. - Gideon Weems

m c
Member #5,337
December 2004
avatar

Me and my friends from uni liked the idea of manually specified sequence points are good I think, that way lines are assumed to be operating in parallel by default, which affects how you program.

I guess that would make auto-vecotrizing compilers easier to work, and allow more parallel type of CPUs to work better, because software would then have to be engineered with parallelism in mind.

Although most of the code that I write is inherently sequential anyway, or at least the dependencies are quite complicated.

I like the eval() statement from dynamic languages, that is great. Would have been cool if C had included a C JIT compiler in the stdlib, like stdjit.h or something, that allowed compiling, linking and executing c strings of c code at runtime, though that would make the language less portable I am sure. Would be nice in a few cases though.

I like the signal / slot thing in QT C++.

I like the numerical ranged types in languages such as ADA.

I liked how in watcom C you could define custom calling conventions. I found that to be cool.

I find the concept of domain specific languages interesting, though I honestly find it silly to try to do that using C++ template meta programming.

(\ /)
(O.o)
(> <)

Gideon Weems
Member #3,925
October 2003

This is gonna sound awful, but I like goto. I have never used it in a C program--not once. Rather, I feel safe just knowing that goto is there, ready to get me out of any jam at a moment's notice. At the same time, I hope that I never have to use it.

Goto is like keeping a fire extinguisher in the house.

Otherwise, I'm a fan of anything that saves work. If more work now means less in the long run, I'm all for it--and the less that more work is, the happier I am.

Chris Katko
Member #1,881
January 2002
avatar

goto receives too much hate from people who are "half good" at programming.

goto is a useful construct. There's a reason it exists in many languages. If goto makes a structure more obvious to the reader than rows of if's and switch statements, then use it. If it makes sense? Use it.

Like you said, it's a "last resort" because using it often means you're losing all of the benefits of OOP. The biggest one is that it violates your intuition of scope. You have no way to easily tell if you're leaving things dangling and unfinished. But just because it "can" be bad, doesn't mean it is always. Pointers can be bad in the same, if not more ways than goto, but we use them every day. There's just more typical benefits from pointers to justify their danger. So everyone in general is magically okay with pointers.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Gideon Weems
Member #3,925
October 2003

You make a good point. I seem to remember a famous programmer saying something similar about dynamic memory allocation (though I'm unable to provide a reference).

Slartibartfast
Member #8,789
June 2007
avatar

#SelectExpand
1int foo(int **ppiFood) 2{ 3 int retcode = STATUS_UNDEFINED; 4 int *piFood = NULL; 5 6 DEBUG_PRINT("foo called"); 7 8 piFood = malloc(sizeof(*piFood)*10); 9 if (NULL == piFood) 10 { 11 DEBUG_PRINT("foo: malloc failed"); 12 retcode = STATUS_ERROR_FOO_MALLOC; 13 goto lblFinish; 14 } 15 16 retcode = bar(piFood); 17 if (FAILED(retcode)) 18 { 19 DEBUG_PRINT("foo: bar failed"); 20 goto lblFinish; 21 } 22 23 **ppiFood = piFood; 24 piFood = NULL; 25 retcode = STATUS_SUCCESS; 26 27lblFinish: 28 29 if (NULL != piFood) 30 { 31 free(piFood); 32 } 33 34 DEBUG_PRINT("foo returns"); 35 36 return retcode; 37}

Noble use of the goto.

bamccaig
Member #7,536
July 2006
avatar

I routinely use goto in a sort of catch and finally way. Errors jump to a catch label which jumps to a finally label, but normal execution falls through to finally and returns. Saves on redundant cleanup.

m c
Member #5,337
December 2004
avatar

Yeah goto is excellent for goto badend; type of uses, and also for multi-level breaks.

sometimes a reverse goto is good too.

Someone once said that reverse goto (going to a spot earlier than where you are) is never necessary, and that code can always be restructured to not need it, that MIGHT be true, but by making the code more convoluted in some cases, I used reverse goto to good effect when I was experimenting with setcontext.

(\ /)
(O.o)
(> <)

Striker
Member #10,701
February 2009
avatar

My most important paradigma is to write programs for executability, mot readability. Speed goes over readability and maintainability as i am a private person and no other people needs to fiddle around with my code. I add useful comments and speaking variable names, so that i later find through.

For efficiency it is important to use as less functions as possible, because a function call is expensive. It costs time and space (~2 KB each for local variables). For the compiler a method is a function. Thats why with C++ you tend to use more functions. If you call a function in a loop 1000 times you end up with a "gigantic" waste of ressources.

Considered that the most efficient program would be in C with only main, no functions. Naturally you don't always need the highest speed. But in game programming it is useful to optimize the code in time critical parts. 8-)

Chris Katko
Member #1,881
January 2002
avatar

Striker said:

For efficiency it is important to use as less functions as possible,

No no no no no no no no no!

Programs should be written to be portable and to last. System function calls take up huge vasts of time. When a single graphics drawing routine costs more than half your game logic, you should be picking a design pattern that allows the most logical and straight-forward layout of the problem you're trying to solve.

If that wasn't the case, most design patterns wouldn't exist because they involve overhead.

1) Programs may be "slower" than older generation ones because of that. But they can benefit greatly from it. If speed was wholly important, we'd still be running DOS and have direct framebuffer access.

2) System calls and system API's are ridiculusly more exhaustive than they used to be. So the same OLD program will take more today than it did before, for example, QMMP (an MP3 player) on first release VS QMMP today running on PulseAudio. Where PulseAudio, a software mixer for Linux, takes a bloody 10-30% of my netbook's CPU.

3) Threading and memory access can blow up your code's time much more than the sum of it's individual parts.

I'm all for GOTO, but it's not for CPU reasons. It's for logical design reasons. Unless I'm running an embedded controller, or an emulator, the structure is way more important than speed of individual lines of code because you should never optimize before you need to:

Donald Knuth said:

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil"[2]

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Audric
Member #907
January 2001

Striker said:

a function call is expensive. It costs time and space (~2 KB each for local variables).

ôÔ ??? 2Kb sounds huge. Local variables and parameters use space relative to their size, so either your sub-function is allocating lot of things on the stack (and it would cost exactly the same space if the code was in the parent function), or you're passing raw objects as arguments instead of pointers.

Striker said:

If you call a function in a loop 1000 times you end up with a "gigantic" waste of ressources.

Hmm, in terms of space, no, you re-use 1000 times the exact same piece of memory. And in terms of time, no, unless you accidentally pass a lot of data as arguments, because they get copied to the stack. Ex: passing an array of 40000 characters (3.9Kb), instead of passing the address of the first character (8 byte)

bamccaig
Member #7,536
July 2006
avatar

The overhead of a function call is negligible. If your program is so performance sensitive that it can't handle any function calls then you're either on a very crappy embedded system or you're doing it very, very wrong.

Thomas Fjellstrom
Member #476
June 2000
avatar

If any of you think he was serious... You all need help.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Striker
Member #10,701
February 2009
avatar

I knew this would start a discussion, especially with those who have C++ as holy cow. Sorry i am not commenting everything in detail.

Chris, different people have different paradigmas. For you it is portability, for me it is to get maximal speed. Thats OK.

Audric, i have read 2 KB somewhere. It will be different at different compilers. There are a few things stored like an adress to return after the function and so on. These 2 KB become allocated on the heap or stack and later freed. But it is all activity that makes the program slower.

Bamccaig, a few thousand negligible things can build a sum which is not negligible anymore.

Thomas, you mean me? It`s my full seriousity.

After all, if you have two programs doing the same, one has all in main and the other has a few thousand function calls you can measure a speed difference, thats sure. ;D

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

bamccaig said:

The overhead of a function call is negligible. If your program is so performance sensitive that it can't handle any function calls then you're either on a very crappy embedded system or you're doing it very, very wrong.

This ^-^

l j
Member #10,584
January 2009
avatar

A function call will put the return address of the previous method on the stack, that's 4 or 8 bytes. When calling a method with no paramaters you write 8 or 16 bytes to the stack (return address + object pointer). The stack is a fixed size and thus never dynamically reallocates. Everything on the stack is nicely packed together and as far as I'm aware quite cache friendly. Functions/methods also get cached. Avoiding using functions is about the most stupid thing I've heard so far.

Apart from passing whole arrays by value I can not imagine how you'd get to the 2 KB value, debuggers will add extra information, but certainly not that much.

Chris Katko
Member #1,881
January 2002
avatar

Striker said:

After all, if you have two programs doing the same, one has all in main and the other has a few thousand function calls you can measure a speed difference, thats sure. ;D

Post the results.

An operating system pre-empting your program to run another, or an interrupt, is going to cause more of a delay.

Many people don't realize that CPU's are extremely fast... but they take a long time to "charge up." To fill the pipeline and prediction tables. And throwing away your entire pipeline every time you task switch is going to be a bigger problem than saving/restoring the function stack to cache--which costs on the order of ~20 cycles so if you're running 500 cycles of code in a function, than you're dwarfing the overhead. So if the thing "we can't control", that is, task scheduling, is dwarfing the time it costs to use a function, then by extension, the costs of functions don't matter. (*DUH, we're talking about most cases, not extreme 1000 deep recursion of single line functions.)

Moreover, many compilers inline functions automatically. What's an inlined function's overhead? Zero.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

bamccaig
Member #7,536
July 2006
avatar

Chris Katko
Member #1,881
January 2002
avatar

bamccaig said:

ahem You were saying?

And even if he is, I find discussions on paradigm overhead to be both fun and informative. Though, technically, this thread was supposed to be about useful / lesser known programming paradigms more than overhead.

But this forum is so small these days all threads kind of merge together.

[edit] One sec, while I fix das code

Code for 100 million runs of 10 distance calculations on doubles:

Test case 1 - No functions:

#SelectExpand
1#include <cmath> 2#include <iostream> 3 4using namespace std; 5 6int depth = 1; 7double d = 0; 8 9int main() 10{ 11 double x = 5; 12 double y = 10; 13 14 for(int i = 0; i < 100000000; i++) 15 { 16 if(depth > 0)depth++; //comparable if statement and increment (see next code) 17 d = sqrt(x*x + y*y); 18 d = sqrt(x*x + y*y); 19 d = sqrt(x*x + y*y); 20 d = sqrt(x*x + y*y); 21 d = sqrt(x*x + y*y); 22 d = sqrt(x*x + y*y); 23 d = sqrt(x*x + y*y); 24 d = sqrt(x*x + y*y); 25 d = sqrt(x*x + y*y); 26 d = sqrt(x*x + y*y); 27 } 28 cout << "Done." << endl; 29}

Test Case 2, 5, and 10 recursive function calls each iteration of 10 distance calculations:

#SelectExpand
1#include <cmath> 2#include <iostream> 3 4using namespace std; 5 6double d = 0; 7 8void my_function1( int depth) 9 { 10 if(depth > 0) 11 { 12 depth--; 13 my_function1(depth); 14 } 15 } 16 17int main() 18{ 19 double x = 5; 20 double y = 10; 21 22 for(int i = 0; i < 100000000; i++) 23 { 24 my_function1(2); //5 and 10 for other cases, hardcoded so we don't incur a variable penalty. 25 d = sqrt(x*x + y*y); 26 d = sqrt(x*x + y*y); 27 d = sqrt(x*x + y*y); 28 d = sqrt(x*x + y*y); 29 d = sqrt(x*x + y*y); 30 d = sqrt(x*x + y*y); 31 d = sqrt(x*x + y*y); 32 d = sqrt(x*x + y*y); 33 d = sqrt(x*x + y*y); 34 d = sqrt(x*x + y*y); 35 } 36 cout << "Done." << endl; 37}

Compiled exactly the same.

g++ main1.cpp -o main1 
g++ main5.cpp -o main5 //etc

Results:

#SelectExpand
1$ time ./main1 2Done. 3real 0m9.431s 4user 0m9.423s 5sys 0m0.000s 6 7// 2 deep 8$ time ./main2 9Done. 10real 0m10.724s 11user 0m10.706s 12sys 0m0.004s 13 14// 5 deep 15$ time ./main5 16Done. 17real 0m12.154s 18user 0m12.135s 19sys 0m0.004s 20 21 22// 10 deep 23$ time ./main10 24Done. 25real 0m15.869s 26user 0m15.868s 27sys 0m0.004s

And that's 10 deep, for a function computing only 10 sqrt's. I can't think of any paradigm that would be more than a couple calls deep. Someone can modify it to be indirect (virtual) calls if they like. Those will be slower. But again, they're never ten deep.

[edit]

Results for -O0 disabled optimizations.

#SelectExpand
1novous@ubuntu:~/Desktop/dev/speedtest$ time ./main1 2Done. 3 4real 0m9.939s 5user 0m9.943s 6sys 0m0.000s 7novous@ubuntu:~/Desktop/dev/speedtest$ time ./main2 8Done. 9 10real 0m11.000s 11user 0m10.993s 12sys 0m0.000s 13novous@ubuntu:~/Desktop/dev/speedtest$ time ./main5 14Done. 15 16real 0m12.867s 17user 0m12.850s 18sys 0m0.003s 19novous@ubuntu:~/Desktop/dev/speedtest$ time ./main10 20Done. 21 22real 0m14.797s 23user 0m14.797s 24sys 0m0.000s

And even funnier, with -O3, the functions are faster! Perhaps G++ doesn't expect you to not use functions. But it's probably eating redundant data, so careful interpreting those results.

#SelectExpand
1time ./main1 2Done. 3 4real 0m0.128s 5user 0m0.128s 6sys 0m0.000s 7novous@ubuntu:~/Desktop/dev/speedtest$ time ./main2 8Done. 9 10real 0m0.007s 11user 0m0.000s 12sys 0m0.007s 13novous@ubuntu:~/Desktop/dev/speedtest$ time ./main5 14Done. 15 16real 0m0.003s 17user 0m0.003s 18sys 0m0.000s 19novous@ubuntu:~/Desktop/dev/speedtest$ time ./main10 20Done. 21 22real 0m0.003s 23user 0m0.003s 24sys 0m0.000s

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Thomas Fjellstrom
Member #476
June 2000
avatar

I still don't think he's serious.

For one, the optimizer will have a fit with any sufficiently large function and it will start producing some pretty crappy code.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Polybios
Member #12,293
October 2010

I like Lua's tables.

 1   2   3   4 


Go to: