|
|
| Programming Paradigms and Useful Features |
|
Chris Katko
Member #1,881
January 2002
|
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: |
|
Thomas Fjellstrom
Member #476
June 2000
|
I'm pretty sure ObjC's arguments are not re-orderable. the actual name of the symbol is baked into the argument's order. -- |
|
Slartibartfast
Member #8,789
June 2007
|
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
|
Thomas Fjellstrom said: I'm pretty sure ObjC's arguments are not re-orderable. Noted, I'm not actually familiar with Objective-C. Only Verilog. -----sig: |
|
Steve Terry
Member #1,989
March 2002
|
not the first time I've seen named parameters, I believe OS/390 even had them. ___________________________________ |
|
m c
Member #5,337
December 2004
|
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. (\ /) |
|
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
|
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: |
|
Gideon Weems
Member #3,925
October 2003
|
|
Slartibartfast
Member #8,789
June 2007
|
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
|
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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
|
m c
Member #5,337
December 2004
|
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. (\ /) |
|
Striker
Member #10,701
February 2009
|
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.
|
|
Chris Katko
Member #1,881
January 2002
|
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: |
|
Audric
Member #907
January 2001
|
Striker said: a function call is expensive. It costs time and space (~2 KB each for local variables).
ôÔ 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
|
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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
|
Thomas Fjellstrom
Member #476
June 2000
|
If any of you think he was serious... You all need help. -- |
|
Striker
Member #10,701
February 2009
|
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.
|
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
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 ^-^ My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
l j
Member #10,584
January 2009
|
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
|
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. 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: |
|
bamccaig
Member #7,536
July 2006
|
Thomas Fjellstrom said: If any of you think he was serious... You all need help. ahem You were saying? -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
|
Chris Katko
Member #1,881
January 2002
|
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: 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: 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: 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. 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. 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: |
|
Thomas Fjellstrom
Member #476
June 2000
|
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. -- |
|
Polybios
Member #12,293
October 2010
|
I like Lua's tables. |
|
|
|