memswap()

M

Malcolm

Why isn't

void memswap(void *ptr1, void *ptr2, size_t len)

part of the standard library?

The function is easy to write, but if it was part of the standard then you
could write code like

memswap(&x, &y, sizeof(double));

knowing that any half-decent compiler would optimise it to a few inlined
instructions.
 
T

Thomas Matthews

Malcolm said:
Why isn't

void memswap(void *ptr1, void *ptr2, size_t len)

part of the standard library?

The function is easy to write, but if it was part of the standard then you
could write code like

memswap(&x, &y, sizeof(double));

knowing that any half-decent compiler would optimise it to a few inlined
instructions.

Your memswap declaration allows the function to swap
any sized area of memory with another.

This leads to the issue of the implementation having
enough memory to swap the areas. What about something
like:
unsigned char * p;
unsigned char * q;
p = malloc(32 * 1024 * 1024);
q = malloc(32 * 1024 * 1024);
memswap(p, q, 32 * 1024 * 1024);

Also, what happens if the size is bigger than
the size of the first two arguments:
unsigned int i;
unsigned int j;
memswap(&i, &j, 2 * sizeof(int));

Some compilers may already have a "swap" function.
 
B

Brett Frankenberger

Your memswap declaration allows the function to swap
any sized area of memory with another.

This leads to the issue of the implementation having
enough memory to swap the areas.

How much do you think it would need?
What about something like:
unsigned char * p;
unsigned char * q;
p = malloc(32 * 1024 * 1024);
q = malloc(32 * 1024 * 1024);
memswap(p, q, 32 * 1024 * 1024);

void memswap(void *p1, void *p2, size_t count)
{
char tmp, *c1, *c2;

c1 = p1;
c2 = p2;
while (count--) {
tmp = *c1;
*c2++ = c1;
*c1++ = tmp;
};
};

Or, if you really want to get fancy, make them unsigned chars and do:
*c2 ^= *c1;
*c1 ^= *c2;
*c2++ ^= *c1++;
and eliminate the temporary variable.
Also, what happens if the size is bigger than
the size of the first two arguments:
unsigned int i;
unsigned int j;
memswap(&i, &j, 2 * sizeof(int));

Presumably undefined behavior, just like if you use memcpy() to copy
beyond the end of an object.

-- Brett
 
B

Ben Pfaff

Or, if you really want to get fancy, make them unsigned chars and do:
*c2 ^= *c1;
*c1 ^= *c2;
*c2++ ^= *c1++;
and eliminate the temporary variable.

And make swap(p, p, n) degenerate into memset(p, 0, n) in the
process, too.
 
D

Derk Gwen

# Why isn't
#
# void memswap(void *ptr1, void *ptr2, size_t len)
#
# part of the standard library?

Because nobody compaigned hard enough to add it? Or it was even defined the
last time standards round?

# knowing that any half-decent compiler would optimise it to a few inlined
# instructions.

A compiler can add it and optimise it whether the standard mentions it or not.

Note that putting & in front of variable usually forces the variable into memory
and not permit it to live in a register. Perhaps the compiler can decide it doesn't
really need the address and allow register allocation, but this could also cost
far more than the possible optimisation of this one operation.
 
B

Ben Pfaff

It's clear what should happen when swapping two buffers that
overlap completely; that is, nothing at all should happen. It is
not clear to me what should happen when swapping two buffers that
partially overlap. Any suggestions?
 
B

Brett Frankenberger

It's clear what should happen when swapping two buffers that
overlap completely; that is, nothing at all should happen. It is
not clear to me what should happen when swapping two buffers that
partially overlap. Any suggestions?

Undefined behavior, just like memcpy(). (memcpy(), by the way, is
undefined if the areas overlap, even if they overlap completely.)

-- Brett
 
A

Arthur J. O'Dwyer

[And screw up the case where c1==c2]
Hm, the way I see it:
(on entry)
c1 = 1, c2 = 1
c2 ^= c1
c1 = 1, c2 = 0

You forgot the dereference operators. If c1==c2, then
*c1 and *c2 are the same object. From the step above:

*c2 ^= *c1
*c1 = 0, *c2 = 0

....and continue from there.


And yes,

memswap(p, p+1, 2*sizeof *p)

and similar overlaps should be undefined behavior, as there
is no one reasonable effect this function call could
produce. I think it's simple enough to define the null
case, though (swapping *p with itself is a no-op).

-Arthur
 
B

Brett Frankenberger

Hm, the way I see it:
(on entry)
c1 = 1, c2 = 1
c2 ^= c1

I didn't write that. I wrote
*c2 ^= *c1;
If c1 == c2, then this is equivalent to:
*c2 ^= *c2
and when you exclusive-or anything with itself, you get 0.
Did I miss something here?

Yes. You presented evidence that the "XOR" trick for swapping two
values without needing a temporary variable works even with both values
are identical. But no one was suggesting that wasn't the case.

The c1==c2 case isn't swapping two variables that happen to have equal
values (that woulbe the *c1==*c2 case). The c1==c2 cause is swapping
the same variable with itself. My code doesn't work in the overlapped
case; I'd argue that that's not a problem because, except for the
completely overlapping case, it would be difficult to say what the
result should be anyway.

-- Brett
 
P

Paul Hsieh

Malcolm said:
Why isn't

void memswap(void *ptr1, void *ptr2, size_t len)

part of the standard library?

The function is easy to write, but if it was part of the standard then you
could write code like

memswap(&x, &y, sizeof(double));

knowing that any half-decent compiler would optimise it to a few inlined
instructions.

Well, since nobody else will answer your question, I'll give it a
shot. For small sized types like this, this is how I deal with the
issue:

#define swapType (type, x, y) { \
type tmp_NAMESPACE_OBFUSCATION_XJgb75b6978t; \
tmp_NAMESPACE_OBFUSCATION_XJgb75b6978t = x; \
x = y; \
y = t; \
}

This allows the compiler to use type specific mechanisms to perform
the swap. This matters on processors like the Athlon which physically
seperates that FPU and INT portions of the CPU (if you use an opaque
mechanism for trying to move the data, you may incurr large
performance penalties because of this.)

I think the compiler can use FP registers in something like:

memswap(&x, &y, sizeof(double));

as you suggest, but the compilers ability to do this might be very
narrow. For example if you did this:

memswap(&t.x, &t.y, sizeof(double));

The vendor might conclude that it shouldn't do this if t is a union.
Using FP registers might *normalize* the value while swapping it,
which would make the integer images change in unexpected ways. While
the ANSI committee could write all sorts of irrelevant verbiage about
how something is undefined, or not, their rubber stamp will not
override the desire of vendors who wanted to both simplify the
implementation (and not perform the optimization you are hoping for)
and make sure it was bit preserving to follow the principle of least
surprise.

Using the macro I showed above, you could instead use the following
alternative:

swapType (double, x, y);

Which takes fewer keystrokes, is more flexible, doesn't take a
function call hit, etc, etc.

I discuss various ways of swapping on my website:

http://www.azillionmonkeys.com/qed/case3.html
 
R

Richard Bos

Well, since nobody else will answer your question, I'll give it a
shot. For small sized types like this, this is how I deal with the
issue:

#define swapType (type, x, y) { \
type tmp_NAMESPACE_OBFUSCATION_XJgb75b6978t; \
tmp_NAMESPACE_OBFUSCATION_XJgb75b6978t = x; \
x = y; \
y = t; \
}

That deals with the wrong issue. I'd expect memswap to be used more
often like this:

memswap(buffer1, buffer2, amount_of_data * *buffer1);

That is, I'd expect it to be used to swap entire buffers, not single
objects, which is what your macro does (correctly).

Richard
 
K

Kevin Easton

Paul Hsieh said:
Well, since nobody else will answer your question, I'll give it a
shot. For small sized types like this, this is how I deal with the
issue:

#define swapType (type, x, y) { \
type tmp_NAMESPACE_OBFUSCATION_XJgb75b6978t; \
tmp_NAMESPACE_OBFUSCATION_XJgb75b6978t = x; \
x = y; \
y = t; \
}

Here's another (better?) way of getting a name that's not supposed to
clash with x or y:

#define swap(type, x, y) \
{ \
type tmp ## x ## y; \
tmp ## x ## y = x; \
x = y; \
y = tmp ## x ## y; \
}

It would be nice if the standard included the GNU C typeof keyword, wouldn't
it?

- Kevin.
 
E

Eric Sosman

Kevin said:
Here's another (better?) way of getting a name that's not supposed to
clash with x or y:

#define swap(type, x, y) \
{ \
type tmp ## x ## y; \
tmp ## x ## y = x; \
x = y; \
y = tmp ## x ## y; \
}

Try it with `swap(sometype, array, array[j])' and
let us know what your compiler thinks ...
 
M

Malcolm

Paul Hsieh said:
Well, since nobody else will answer your question, I'll give it a
shot. For small sized types like this, this is how I deal with the
issue:
[ swap macro using type and temporary ]
None of these swap macros have caught on. One of the reasons I think is that
C people don't like to pass types to function-like macros (an exception is
the va_list macros). Another reason is that if you are swapping two
pointers, it isn't obvious whether you should swap the pointers or what they
point to. A first reason could be fixed, you should pass the address of a
variable to be changed to show that its value may have been altered.
I think the compiler can use FP registers in something like:

memswap(&x, &y, sizeof(double));

as you suggest, but the compilers ability to do this might be very
narrow. For example if you did this:

memswap(&t.x, &t.y, sizeof(double));

The vendor might conclude that it shouldn't do this if t is a union.
Time for some anti-union legislation. Seriously, unions aren't used often
enough for this to be a problem in itself, but it might make an optimising
compiler hard to write if it has to check for unions before doing the
optimisation.
Using the macro I showed above, you could instead use the following
alternative:

swapType (double, x, y);

Which takes fewer keystrokes, is more flexible, doesn't take a
function call hit, etc, etc.
I think that is a valid criticism of memswap() - it is rather too many
keystrokes when C++ has swap(x,y).
 
B

Ben Pfaff

Undefined behavior, just like memcpy().

Seems to me like it would be more useful if it could handle
overlap.
(memcpy(), by the way, is undefined if the areas overlap, even
if they overlap completely.)

But there is also memmove() that handles overlap properly.
 
B

Brett Frankenberger

Seems to me like it would be more useful if it could handle
overlap.

Maybe just the degenerate case of complete overlap, which is easy to
take care of (if (c1==c2) return;).
But there is also memmove() that handles overlap properly.

In the case of partial overlap, what would be the "proper" thing for
memswap() to do.

-- Brett
 
B

Ben Pfaff

Maybe just the degenerate case of complete overlap, which is easy to
take care of (if (c1==c2) return;).


In the case of partial overlap, what would be the "proper" thing for
memswap() to do.

Why are you asking me the same question that I originally asked?
We are going in circles.
 
A

Arthur J. O'Dwyer

Ben Pfaff said:
(e-mail address removed) (Brett Frankenberger) writes:
It's clear what should happen when swapping two buffers that
overlap completely; that is, nothing at all should happen. It is
not clear to me what should happen when swapping two buffers that
partially overlap. Any suggestions?

Undefined behavior, just like memcpy().

Seems to me like it would be more useful if it could handle
overlap.

In the case of partial overlap, what would be the "proper" thing for
memswap() to do[?]

Why are you asking me the same question that I originally asked?
We are going in circles.

Probably as a rhetorical device. What *do* you expect the output
of memswap(p,q,10) to be, given


ABCDEFGHIJKLMNOPQRSTUVWXYZ
^ ^
p q

Since there's no reasonable answer, the answer must be "undefined
behavior."

-Arthur
 
E

Eric Sosman

Arthur J. O'Dwyer said:
[...]
Probably as a rhetorical device. What *do* you expect the output
of memswap(p,q,10) to be, given

ABCDEFGHIJKLMNOPQRSTUVWXYZ
^ ^
p q

AGHIJKLMNOPQRSTUVWXYZBCDEF
Since there's no reasonable answer, the answer must be "undefined
behavior."

Reasonability dwells with Beauty: in the eye of
the beholder.
 
K

Kevin Bracey

In message <[email protected]>
Derk Gwen said:
Note that putting & in front of variable usually forces the variable into
memory and not permit it to live in a register. Perhaps the compiler can
decide it doesn't really need the address and allow register allocation,
but this could also cost far more than the possible optimisation of this
one operation.

I can't speak for any other compilers, but the Norcroft compiler I'm familiar
with does try very hard to prevent automatic variables having their
"address-taken" property set unnecessarily, because of the performance hit
that results from it.

This includes using CSE analysis to elide pointers and addresses, specific
elimination of pointers to local variables passed into inlined functions,
making sure array subscripting doesn't inherently address-take, and a final
rescan to check the "address" pseudo-instruction hasn't been optimised out of
the function altogether.

Any compiler that can strongly optimise non-address-taken variables would
benefit from such pains being taken; "&" in itself shouldn't push something
out of a register.

In our case it's worth worrying about structures as well as scalars, as if
the structure doesn't have its address taken, we can perform structure
splitting (treating the structure as multiple independent
register-allocatable variables).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,077
Messages
2,570,566
Members
47,202
Latest member
misc.

Latest Threads

Top