Android Project Changeset 4f8b683: libc/memset.c
review.source.android.com
review.source.android.com
Is this actual shipping code? I've followed the changeset up to the top and it looks like it's in platform/bootable/bootloader/legacy. Hopefully this isn't actually called anywhere, but the review date says May 13th.
What's the point of optimizing the Java VM when functions like memset, malloc, memcmp, memcpy, memchr, etc, etc haven't even been optimized for the platform.
EDIT: I understand that 95% of your time will be writing in a language like Ruby, Python, Javascript or a C based language. But dear god, please add Agner Fog's Optimization Manuals to your reading list. http://www.agner.org/optimize/
My ARM assembly is a little rusty, but it looks like they implement memset and bzero, the latter just calling memset with R1=0.
Seeing memset is usually a sign that someone is being lazy about watching the length of their buffers and/or verifying their copies aren't off-by-one.
int i;
for (i = 0; i < SIZE; ++i) {
buffer[i] = 0;
}
Instead of: memset(buffer, 0, SIZE)
Because it indicates better buffer management.If you need an array zeroed on first use, why didn't you use calloc? The only reason memset has for existing is when you need to reinitialize an existing array to zero. If things are so tight that you're wiping out an existing array and reusing it, which usually involves being deliberately less abstract than you'd naturally be, then, well, that's a pretty unusual situation.
(While we're on memset, I'd like to point out that the weirdest thing is that while memset initializes value byte by byte, the places where you do need a prefilled array are always places where the array is an array of larger values.)
Setting memory you own to a known value is just good, defensive programming. If I screw up - and in C, you're going to screw up - it's good to see a known value rather than unknown values.
I'm all for defensive programming, but when you need to be fast, you'll just have to make sure it's correct.
In fact I bet the performance would be worse, especially because you have to worry about setting areas of memory that are not a multiple of the loop unroll size. (I guess you could use duff's device.)
I actually tested it while ignoring that, and the unrolled version took 34.551 seconds vs 34.239 for the regular version (but those number are meaningless since the variation in time between runs is greater than the difference in runtime between versions).
And see: http://lkml.indiana.edu/hypermail/linux/kernel/0008.2/0171.h... - loop unwinding is not worth it anymore.
So I ask you again pkaler: how would you speed this up?
Integrating memset/bzero logic into memory array itself will increase it's price drastically (but in some special cases it is done, generally when memory array already contains some other expensive non-memory logic).
why can't you have a '#ifdef PREFER_SIZE_OVER_SPEED' or something similar ?
Of course you could do all that, but it's so much easier to just override the 5 functions you actually use in your code. Because of the way linkers work and the way standard libraries are designed, if you define your own version of a library function then the linker won't pull in the library version--effectively choosing your local version over the one in the library. That keeps the standard library clean of patches and keeps the small, tight code near the project that actually needs it.
The shown example, that just writes to RAM, is probably just fast enough (it is harder to optimize read cases), although a bit of unrolling could reduce jump penalty impact (specially on non superscalar or in in-order superscalar CPUs -typical ARM included on handheld devices-).
FWIW, your definition of "halfway decent" would exclude most software shops in the world.
It is extremely unprofessional to disable or ignore warnings such as the "unused function parameter" warning
Unused function parameter warnings are often noise (e.g., due to #ifdefs, unimplemented APIs, backward compatible APIs, etc). I agree that code should compile w/o warnings, but the benefits of tracking down and squelching unused function argument warnings are pretty marginal in my experience.
"-W -Wall -Wcast-align -Wstrict-prototypes -Wmissing -prototypes -Wpointer-arith -Wshadow -Wsign-compare -Wformat=2 -Wno-format-y2k -Wimplicit -Wmissing-braces -Wnested-externs -Wparentheses -Wtrigraphs"
Update: Forgot the most important one ... -Werror. Making warnings equivalent to errors is the only way to ensure that they're always taken care of.
The benefits of tracking down and squelching such warnings would have very likely caused this bug to never exist. The point is that you make "compiles without warnings" a policy, and you build a habit of maintaining that policy -- after a while it begins to take almost no effort at all.
Also, unless every programmer on the project is being scrupulous about finding and correcting such errors, it quickly becomes line noise, and a lot of it completely unimportant stuff.
This is practically a documentation bug.
[edit]
I kind of take it back, after mahmud's bzero() joke... out of oniguruma, Python, hexfiend, Apache, nginx, openssl, redis, regexkit, saxon, memcached, subversion, and valgrind, this hypothetical bug would have broken Python, redis, subversion, several apache modules, and memcached. The contents of my codebase/3p directory, FWIW.
$ echo 'int main(int argc, char **argv) { if (argc < 0) { return 1; } else {return 0;}}' > test.c
$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.3-4ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5)
$ gcc -Wall -Wextra test.c
test.c: In function ‘main’:
test.c:1: warning: unused parameter ‘argv’
$
gcc-4.3.4 behaved the same way in my test.Another potential issue is that the code in the patch might almost never be called in the first place. Most libc implementations (three that I know of, where I've examined the "memset" source) have a "memset" like that as a fallback only for platforms without an assembly version.
I'd say the problem is that there are several different ways this error could have been detected sooner: warnings, test, static analysis tools, and review, but none of them worked. But again, it's also possible that the code is never called because assembly versions are always available.
Well, so then you're exposed to this error. Do:
void foo(int x)
{
(void)x;
}
to silence unused parameter warnings.Very little C code is const-correct either. Most of the C devs I know make fun of const-correctness.
It's only an eyesore if you don't acknowledge the benefits that strict warnings can bring to the table.
| Most of the C devs I know make fun of const-correctness.
I for one don't mind putting in a tiny bit of upfront effort so that my compiler can double-check my work. Do the C devs you know also make fun of assert?
And, no. Nobody makes fun of assert. Which rather makes my point for me.
I'm not as dogmatic as some others are in the thread. I just think it's a good idea to be warned when a variable is unused, enough that I'll mark my very few that might be #ifdef'ed out. If you're using a compiler that doesn't let you mark variables as such... well that sucks. You do what you can.
int foobar(int foo, int bar __attribute__((unused))) { /* Code that does not use bar */}
As a point of pride, none of them were created by me, but it is what it is. Some of them come from since deprecated functions and nobody wants to go back and mess with old code; some of them come from sloppiness; some of them come from java's collection handling.
And yet this codebase gets a lot of work accomplished anyway.
It's more helpful in C++, which can omit parameter names to indicate that it's intentionally unused. But then you're using C++; not worth the tradeoff.
Such as?
1. An API-defined callback signature where you need to use (for example) the first and third parameters, and not the second.
2. A public API function that has become deprecated and the previous behavior is emulated in a way that doesn't require all the parameters.
3. Stub/wrapper functions.
4. Init/cleanup functions for modules written to a plugin API, when all parameters passed are not needed in all cases.
5. Language binding closure/thunk functions.
Most of the uses, I think, boil down to backwards compat and fixed callback/plugin interfaces. In those cases I think an __attribute__((unused)) or void cast aren't a huge burden. I usually turn on -Wunused-parameter just in case.
http://android.git.kernel.org/?p=platform/bionic.git;a=blob;... http://android.git.kernel.org/?p=platform/bionic.git;a=blob;...
Given that the bug went undetected for so long, I think you could almost reasonably claim that calling memset with a nonzero parameter is a "corner case" ;)
So, give it a pointer to some memory, give it a value and the number of bytes you want to set and it'll go wild. In this case, memset was just blindly dropping in zeroes into the memory block
Instead of special casing memzero as memset(p,0,count);