|
|
Subscribe / Log in / New account

C considered dangerous

C considered dangerous

Posted Aug 30, 2018 21:49 UTC (Thu) by zlynx (guest, #2285)
In reply to: C considered dangerous by epa
Parent article: C considered dangerous

Only people using x86 compatible Core2 or later CPUs think this is a good idea.

memcpy and memmove are *NOT IDENTICAL*.

That extra comparison to determine copy direction costs time and branch prediction failure. The only reason everyone seems to think it's free is that common CPU types now run ahead and prime the branch prediction.

I had evidence from oprofile in 2005 that showed memmove was most definitely slower than memcpy. That was on Pentium 3 and 4. Pipeline bubbles on P4 were painful.

Just like all the other sharp edges in C like pointer aliasing, programmers need to *read the documentation* and *know what they're doing* when using memcpy. Having it blow up their program is entirely fair.


to post comments

C considered dangerous

Posted Aug 31, 2018 6:07 UTC (Fri) by epa (subscriber, #39769) [Link] (1 responses)

Having it blow up the program would be fine. But the standard says the behaviour is undefined. That allows for silent memory corruption or indeed anything else. If I wanted something marginally faster but which would magnify the effect of programmer error, I would use plain memcpy(). The whole point of the _s variants is to add extra safety, even if it does cost a couple of extra instructions.

C considered dangerous

Posted Aug 31, 2018 6:40 UTC (Fri) by iabervon (subscriber, #722) [Link]

It would probably be good enough to define memcpy_s() to write to only locations in the destination, and write values that are in the corresponding point in the source either before the operation or afterwards, independently for each location. If you don't realize the buffers overlap, and you intend to keep using the source buffer, memmove doesn't save you anyway. It's worth allowing memcpy_s to have the obvious performance optimization without also allowing the compiler to infer that the buffers don't overlap and start doing really unexpected things.

C considered dangerous

Posted Sep 4, 2018 5:19 UTC (Tue) by ncm (guest, #165) [Link] (7 responses)

Generally nowadays memmove is *faster* than memcpy, by a remarkably wide margin for smallish N.

Complicating the interface for this and similar functions amounts to piling deck chairs on the Titanic into an obstacle course.

It is more than a little stupid to write a new program in C. All the typical pitfalls of pointer manipulations just don't arise in C++. C++ does still have integer overflow UB, as does Rust, and anyway unsigned integers wrapping around is nowhere near so benign as many like to believe.

There would be no problem with C++ in the Linux kernel if not for mindless bigotry, with many fewer opportunities for silly-bugger mistakes, and both faster and shorter code. It will happen eventually. It might happen first in FreeBSD, as the blinkered ancients there are closer to retirement.

C considered dangerous

Posted Sep 4, 2018 6:46 UTC (Tue) by quotemstr (subscriber, #45331) [Link] (5 responses)

> Generally nowadays memmove is *faster* than memcpy, by a remarkably wide margin for smallish N.

That's absurd nonsense. You must be talking about different implementations of memcpy and memmove. so it's not an apples-to-apples comparison. You can always implement memcpy by taking memmove and baking in the memory-direction branches, which means that no matter how efficient your memmove, you can generate an even _more_ efficient memcpy out of it.

I can imagine memmove and memcpy being equally fast, but memmove being faster, by doing more work? Something is clearly wrong with the comparison.

memmove faster than memcpy for small N

Posted Sep 5, 2018 23:03 UTC (Wed) by jreiser (subscriber, #11027) [Link] (4 responses)

The key is "for small N". The glibc implementation of memcpy favors speed for non-small N, and has several checks and conditional branches before doing much "useful work". The penalty for a mis-predicted conditional branch is often about 12 cycles. The implementation of memmove has fewer initial checks (mostly for overlap or not), and starts doing "useful work" sooner. For small N, the fewer mis-predicted conditional branches enable faster memmove.

memmove faster than memcpy for small N

Posted Sep 5, 2018 23:45 UTC (Wed) by quotemstr (subscriber, #45331) [Link] (3 responses)

That makes no sense. A memcpy could turn those branches into unconditional jumps and avoid any mispredict penalties.

memmove faster than memcpy for small N

Posted Sep 6, 2018 3:42 UTC (Thu) by jreiser (subscriber, #11027) [Link] (2 responses)

Code, please. I'll bet a beer that your code will be slower for small N (or incorrect.)

memmove faster than memcpy for small N

Posted Sep 6, 2018 6:55 UTC (Thu) by liw (subscriber, #6379) [Link]

I think both sides need to provide code and benchmarks to settle this question. Hypotheticals and claims based on mental models only go far. Even arguing based on code is insuffident, given how complex an issue performance analysis can be.

I tried writing a benchmark. GCC optimised it away.

memmove faster than memcpy for small N

Posted Sep 6, 2018 7:14 UTC (Thu) by quotemstr (subscriber, #45331) [Link]

No, you write the code. I'm not the one making the absurd claim. A program is never made slower by converting always-taken conditionals into jumps.

C considered dangerous

Posted Sep 21, 2018 18:14 UTC (Fri) by mathstuf (subscriber, #69389) [Link]

> C++ does still have integer overflow UB, as does Rust

No, Rust defines integer overflow as twos-complement (though in debug builds it will panic). You can use an always-panicking addition however, but that's not the default.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds