Reputation: 11
I need to do an Ultra High Performance swap of two pointers in C (the addresses, not what they point to). I know that the addresses are aligned (multiple of 16) and consecutive and I've already checked that this preserves in every execution. The code is as follows:
Nodo a __attribute__((aligned(8))), b __attribute__((aligned(4)));
Nodo *nodo_superior __attribute__((aligned(4)));
nodo_superior = &a;
Nodo * nodo_actual __attribute__((aligned(4)));
nodo_actual=&b;
printf("%ld %ld\n", nodo_superior, nodo_actual);
and the result on console is like (please, note that the first address should be "over"-aligned in order the next method to work):
140594404335200 140594404335216
Now to swap the addresses, I want to change the bit 5 of each pointer:
nodo_superior ^= 16;
nodo_actual ^= 16;
but, unfortunately the C compiler does not allow this because integers can not be mixed with pointer values: it's an error, not a warning. If I try to include the pointer in an union like this:
union {
Nodo * nodo_actual;
int local_int;
} na;
with the idea of doing an assigment like:
na.local_int ^= 16;
then the performance decreases because (I suppose) the compiler cannot apply/infer optimizations to nodo_actual if it is in an union. Any ideas to force the compiler to behave as I want? Of course, I could change the generated assembler but I do not consider it a good option,
Upvotes: 1
Views: 264
Reputation:
A swap is better performed "the old way", with a temporary variable.
Nodo* Swap= a; a= b; b= Swap;
This costs 2 reads and 2 writes.
The "smart" solution
a^= 16; b^= 16;
costs 2 reads, 2 xors and 2 writes.
Upvotes: 2
Reputation: 1826
I'm not sure I really understand what you mean by "swap two pointers". It seems you have a pair of nodes, and you want the compute the address of one node from the address of the other one.
A similar problem is when you have two numbers (for exemple 1 and 2 for player number) and want to change the player number from 1 to 2 and vice-versa. A usual trick is to compute
opponent = (1+2) - player ;
You can play a similar trick with pointers, because some arithmetic is allowed :
int main(int argc, char **argv)
{
struct Node x;
struct Node y;
printf("x is at %p, y at %p\n", &x, &y);
struct Node * one = &x;
struct Node * other = &x + (&y - one); // (&x + &y) - one
printf("if one is at %p, the other is at %p\n", one, other);
one = &y;
other = &x + (&y - one);
printf("if one is at %p, the other is at %p\n", one, other);
return 0;
}
Upvotes: 0
Reputation: 141598
If you want to toggle the bit 5 of each pointer you can write:
nodo_superior = (Nodo *)((uintptr_t)nodo_superior ^ 16);
uintptr_t
is an integer type that (if exists) is guaranteed to be able to hold a pointer value without loss of information.
It's intended that this be done by the obvious isomorphism on a flat memory model, so it should work for you (although test it first to see).
Also, you should be using %p
to printf a pointer, not %ld
which causes undefined behaviour. To print uintptr_t
see here
Upvotes: 2