Funny C Rules (And Low-Level Languages in general)

Discuss programming topics for the various GPL'd game engine sources.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Funny C Rules (And Low-Level Languages in general)

Post by revelator »

just jerking his chains a bit ;) but indeed its an int.

something to look out for if typecasting also since it does not change the underlaying type so results might not be what one would expect.
Hmm one thing that has allways baffled me is byte types or unsigned char, do they also fall back to int or is there more to them ?.
range from experience is 0 - 255 and theres actually a rather nasty bug in the vanilla doom3 font code that assumes a negative byte value,
which every static analyzing tool i have tried on it screams to me that byte values cannot be negative. funny enough most compilers let it pass anyway heh.
Productivity is a state of mind.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by Baker »

Just discovered that CodeBlocks has Swap Headers which switches between source and header. i.e. swap between, as an example, "common.c" and "common.h".

I like that feature in XCode which is default Ctrl + Alt + UP.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by mankrip »

Baker wrote:Just discovered that CodeBlocks has Swap Headers which switches between source and header. i.e. swap between, as an example, "common.c" and "common.h".

I like that feature in XCode which is default Ctrl + Alt + UP.
And if the corresponding header doesn't exist, Code::Blocks will create it automatically. Without asking.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by Baker »

mankrip wrote:And if the corresponding header doesn't exist, Code::Blocks will create it automatically. Without asking.
Oh! ---- Add: Just tried this, it does ask!
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by Baker »

What is the best way to print a date and time to file
--- and read it back.

--- Without spaces in it?
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by mankrip »

Baker wrote:
mankrip wrote:And if the corresponding header doesn't exist, Code::Blocks will create it automatically. Without asking.
Oh! ---- Add: Just tried this, it does ask!
:P Well, I'm still on version 8.02.

[edit] Wait, now I'm not sure if it asks. It's been a while since I've used it to create a header.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by Baker »

Baker wrote:What is the best way to print a date and time to file
--- and read it back.

--- Without spaces in it?
Never mind, sscanf into a time struct
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Funny C Rules (And Low-Level Languages in general)

Post by revelator »

another one to take note of is the pre incremeting and post incrementing used in counters and loops.

this one can be a bit tricky example

int i = 0; ConsoleWrite(++i); = 1 or 1+i
int i = 0 ConsoleWrite(i++); = 0 or 0+i

in a for loop its allways 0 if for (i = 0; i < something; i++ or ++i) unless you do things a bit different :)
so as one can see this can easily fool an unexperienced programmer into beliving that the loop counter starts at 1 and not 0 ;)
Productivity is a state of mind.
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by mankrip »

I don't think it's that easy to fool, because programmers usually know that the increment portion of the loop is only executed after its contents.

However, for (i = 0; i < --something; i++) could fool, due to the comparison being executed before the contents.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Funny C Rules (And Low-Level Languages in general)

Post by revelator »

One should think but i actually seen devs who made that mistake more times than you think hehe.
Theres a fine example of it in the vanilla doom3 sources just try to run clang on it as a static code analyzer and get the popcorn ;).
Honest mistake though since the dev seems to have just copy pastaed the code piece in question but forgot to check the return :).
Productivity is a state of mind.
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by mankrip »

Recently I've come up with a new trick to eliminate some bitshifting operations, replacing something like this

Code: Select all

int i;

while (loopcount--)
{
result = i >> 16;
i += step;
}
... with something like this:

Code: Select all

// "bitscope" the contents of the integer through a short pointer, to get the >>16 division for free.
int i;
unsigned short *s;
s = &i;
s++;

while (loopcount--)
{
result = *s;
i += step;
}
While the code worked perfectly, the performance of the second version is slower, at least in some cases.

Why is it that returning 16-bit data from a memory address pointer is slower than bitshifting the value of a 32-bit variable and returning it?
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Funny C Rules (And Low-Level Languages in general)

Post by Spike »

Memory references should be expected to be slower than (fairly fast) register operations. a bitshift will take only one clock, while memory references can easily take many many more.
Memory access is FAR from free - especially if it means that other locals can no longer be stored in a register either.
The memory accesses also mean that the compiler probably doesn't know whether the value actually changes, so any loop unrolling optimisations go out of the window too.
Also, your code violates C's strict aliasing rules.
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Funny C Rules (And Low-Level Languages in general)

Post by mankrip »

Spike wrote:a bitshift will take only one clock, while memory references can easily take many many more.
The thing is, I couldn't find any C article detailing how many clock cycles it can take to read referenced values. Only x86 Assembly articles, which I haven't learned.
Spike wrote:Memory access is FAR from free - especially if it means that other locals can no longer be stored in a register either.
The memory accesses also mean that the compiler probably doesn't know whether the value actually changes, so any loop unrolling optimisations go out of the window too.
Well, I tried using it in the unrolled inner loops of the rasterizer, to reduce the amount of bitshifting performed in the texture coordinates.
The inner loop is manually unrolled to groups of 16 pixels at once, which means that there were over 32 bitshifting operations (in 16 s coordinates and 16 t coordinates) eliminated through this method. I hoped that this would at least make the code more compact and less prone to cache misses, maybe helping the performance.
Spike wrote:Also, your code violates C's strict aliasing rules.
Which is why I've called it a trick. Reading a signed int through an unsigned short is risky, but the texture coordinates are always ensured to be positive anyway.

At first I had thought of using an union instead of a pointer, but then I've found out that there would be overhead because the union would have to use a struct, which would result in the memory address of the unsigned short having to be calculated from a "s2" offset { int i; struct {unsigned short s1, s2;};}. Using a separate short pointer, the offset can be precomputed.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Funny C Rules (And Low-Level Languages in general)

Post by toneddu2000 »

Spike wrote:a bitshift will take only one clock
:shock: wow, no wonder 80's graphics operations used bitshifting all the times :biggrin:
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Funny C Rules (And Low-Level Languages in general)

Post by Spike »

well, modern cpus have tricks that allow the cpu to do all sorts of things asynchronously, which allows certain things to take less than a clock if its simple enough for the instruction decoding to rearrange things. memory references are far from simple, so try to prevent the compiler from spilling locals to stack
cpus have limited associativity, which means that if you start reading/writing some new location, the cpu will forget somewhere else and re-accessing somewhere that was forgotten will incur a stall as it repopulates from a higher level cache. if that means that it needs to re-read from system ram, then expect a large stall. If you have two CPUs/GPUs trying to write the same memory region then you'll find that they will constantly purge the other's cache of that region (the alternative is worse...).

regarding structs/unions, I wouldn't worry about the offset. if its on the stack then esp+(offset) is identical to esp+(offset+2). globals don't need the esp part, of course. either way you no longer need a separate register to hold the pointer's value, so more registers for your actual maths.

remember that the x86 only has 8 registers (amd64 raises that to 16). and many of them are reserved for specific purposes. So if you need to hold many variables at a time, you'll end up spilling all your variables to stack, and now each reference to those variables will need load+store operations too. constants can often be embedded in the instructions themselves, which helps reduce cache misses etc.

c99's __restrict keyword might be useful to you, as it allows greater freedom by letting the compiler know that writes will not change other memory addresses. this isn't normally an issue for locals if nothing needed an address to them, but your code decided to take an address to it, and now it might need to read the pointer and THEN dereference it as two separate operations. and only then can it read from it.

Or something.

If this stuff is important to you, you should really try and figure out how to use cachegrind - http://valgrind.org/docs/manual/cg-manual.html
For raw clock costs, you should use something like gprof (which requires compiler instrumentation). Expect different results on different cpus (especially but not just with different instruction sets), or even with each run (interrupts etc will flush caches, which will affect reported costs).
Post Reply