Date: Fri, 26 Jul 2013 02:37:14 +1000 (EST) From: Bruce Evans <brde@optusnet.com.au> To: David Chisnall <theraven@FreeBSD.org> Cc: src-committers@FreeBSD.org, svn-src-all@FreeBSD.org, svn-src-head@FreeBSD.org, Tim Kientzle <kientzle@FreeBSD.org>, Bruce Evans <brde@optusnet.com.au>, Hans Petter Selasky <hps@bitfrost.no> Subject: Re: svn commit: r253636 - head/sys/vm Message-ID: <20130726021053.O2628@besplex.bde.org> In-Reply-To: <73FCA347-5EB9-445F-A25C-D06CA137CBEE@FreeBSD.org> References: <201307250348.r6P3mbsG049595@svn.freebsd.org> <20130725171038.O841@besplex.bde.org> <51F0DDB0.7080102@bitfrost.no> <73FCA347-5EB9-445F-A25C-D06CA137CBEE@FreeBSD.org>
next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, 25 Jul 2013, David Chisnall wrote: > On 25 Jul 2013, at 09:11, Hans Petter Selasky <hps@bitfrost.no> wrote: > >> The structure looks like some size, so bzero() might run faster than memset() depending on the compiler settings. Should be profiled before changed! > > They will generate identical code for small structures with known sizes. Both clang and gcc have a simplify libcalls pass that recognises both functions and will elide the call in preference to a small set of inline stores. In the kernel, compilers are prevented from inlining memset() and many other things by -ffreestanding in CFLAGS. This rarely matters, and no one except me cares. In my version, memset() doesn't exist, but bzero() has the micro-optimization of turning itself into __builtin_memset() if the size is small (<= 32). My memcpy() has the micro-optimization of turning itself into __builtin_memcpy() unconditionally. __builtin_memcpy() then turns itself back into extern memcpy() according to the inverse of a similar size check. I think extern memcmp() still doesn't exist in the FreeBSD kernel, so simply using __builtin_memset() would give linkage errors when __builtin_memcmp() turns itself back into memcmp(). Determining whether the size is "small" is difficult. It is very CPU-dependent, and also depends on how efficient the extern function is. Compilers once used a very large limits for inlining, but changed to fairly small limits when they realized that they didn't understand memory. Extern functions are hard to optimize, since the correct optimization depends on the CPU including its cache organization. FreeBSD's x86 bcopy() and bzero() are still optimized for generic CPUs. Generic means approximately the original i386, but since these are important operations, all CPUs run the old i386 code for them not to badly (perhaps only twice as slow as possible), with newer Intel systems doing it better than most. Use of memcpy() in the kernel is the result of previous micro-optimizations. It was supposed to be used only for small fixed-size copies. This could have been done better by making bcopy() inline and calling __builtin_memcpy() in this case. The extern memcpy() should never have been used, but was needed for cases where __builtin_memcpy() turns itself into memcpy(), which happened mainly when compiling with -O0. Other uses of memcpy() were style bugs. No one cared when this optimization was turned into a style bug in all cases by -ffreestanding. > However(), memset is to be preferred in this idiom because the compiler provides better diagnostics in the case of error: > > bzero.c:9:22: warning: 'memset' call operates on objects of type 'struct foo' > while the size is based on a different type 'struct foo *' > [-Wsizeof-pointer-memaccess] > memset(f, 0, sizeof(f)); > ~ ^ > bzero.c:9:22: note: did you mean to dereference the argument to 'sizeof' (and > multiply it by the number of elements)? > memset(f, 0, sizeof(f)); > ^ > > The same line with bzero(f, sizeof(f)) generates no error. This is compiler bug with -ffreestanding. Then memset() is not special. clang allows me to declare my own memset but still prints this warning if the API is not too different: no warning for "void memset(int *, int)", but warning for "int memset(int *, int, int)". The warning seems to be based on the function name, since it is not claimed that the function is standard (even without -ffreestanding). While testing this, I mistyped an &f as &foo, where &foo is a function name. clang doesn't warn about this. clang warned when memset was my home made "int memset(int *, int, int)", because the function pointer isn't compatible with int *. But it also isn't compatible with void *. I think casting NULL to a function pointer must work even if NULL is spelled with a void *, but that is the only case where a void * object pointer can safely be converted to a function pointer. Bruce
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20130726021053.O2628>