diff options
author | Rasmus Villemoes <rv@rasmusvillemoes.dk> | 2015-08-18 11:55:34 +0200 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2015-09-12 21:09:59 -0400 |
commit | 0ce657c576bf1b2436c4e14a002eaf461897d82c (patch) | |
tree | a2fa54f90f91e449ef089921d5c2dfe7f752df7c /sysdeps | |
parent | b482d0364ed55c171708cd95922a896f002b6043 (diff) | |
download | glibc-0ce657c576bf1b2436c4e14a002eaf461897d82c.tar glibc-0ce657c576bf1b2436c4e14a002eaf461897d82c.tar.gz glibc-0ce657c576bf1b2436c4e14a002eaf461897d82c.tar.bz2 glibc-0ce657c576bf1b2436c4e14a002eaf461897d82c.zip |
linux/getsysstats.c: use sysinfo() instead of parsing /proc/meminfo
Profiling git's test suite, Linus noted [1] that a disproportionately
large amount of time was spent reading /proc/meminfo. This is done by
the glibc functions get_phys_pages and get_avphys_pages, but they only
need the MemTotal and MemFree fields, respectively. That same
information can be obtained with a single syscall, sysinfo, instead of
six: open, fstat, mmap, read, close, munmap. While sysinfo also
provides more than necessary, it does a lot less work than what the
kernel needs to do to provide the entire /proc/meminfo. Both strace -T
and in-app microbenchmarks shows that the sysinfo() approach is
roughly an order of magnitude faster.
sysinfo() is much older than what glibc currently requires, so I don't
think there's any reason to keep the old parsing code. Moreover, this
makes get_[av]phys_pages work even in the absence of /proc.
Linus noted that something as simple as 'bash -c "echo"' would trigger
the reading of /proc/meminfo, but gdb says that many more applications
than just bash are affected:
Starting program: /bin/bash "-c" "echo"
Breakpoint 1, __get_phys_pages () at ../sysdeps/unix/sysv/linux/getsysstats.c:283
283 ../sysdeps/unix/sysv/linux/getsysstats.c: No such file or directory.
(gdb) bt
So it seems that any application that uses qsort on a moderately sized
array will incur this cost (once), which is obviously proportionately
more expensive for lots of short-lived processes (such as the git test
suite).
[1] http://thread.gmane.org/gmane.linux.kernel/2019285
Signed-off-by: Rasmus Villemoes <rv@rasmusvillemoes.dk>
* sysdeps/unix/sysv/linux/getsysstats.c (__get_phys_pages):
Use sysinfo system call instead of parsing /proc/meminfo.
* sysdeps/unix/sysv/linux/getsysstats.c (__get_avphys_pages):
Likewise.
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/unix/sysv/linux/getsysstats.c | 88 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/include/sys/sysinfo.h | 26 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/syscalls.list | 2 |
3 files changed, 57 insertions, 59 deletions
diff --git a/sysdeps/unix/sysv/linux/getsysstats.c b/sysdeps/unix/sysv/linux/getsysstats.c index 410f1a975f..5bb5543840 100644 --- a/sysdeps/unix/sysv/linux/getsysstats.c +++ b/sysdeps/unix/sysv/linux/getsysstats.c @@ -278,81 +278,53 @@ __get_nprocs_conf (void) } weak_alias (__get_nprocs_conf, get_nprocs_conf) -/* General function to get information about memory status from proc - filesystem. */ + +/* Compute (num*mem_unit)/pagesize, but avoid overflowing long int. + In practice, mem_unit is never bigger than the page size, so after + the first loop it is 1. [In the kernel, it is initialized to + PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in + kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can + represent all the sizes measured in bytes]. */ static long int -internal_function -phys_pages_info (const char *format) +sysinfo_mempages (unsigned long int num, unsigned int mem_unit) { - char buffer[8192]; - long int result = -1; + unsigned long int ps = __getpagesize (); - /* If we haven't found an appropriate entry return 1. */ - FILE *fp = fopen ("/proc/meminfo", "rce"); - if (fp != NULL) + while (mem_unit > 1 && ps > 1) { - /* No threads use this stream. */ - __fsetlocking (fp, FSETLOCKING_BYCALLER); - - result = 0; - /* Read all lines and count the lines starting with the - string "processor". We don't have to fear extremely long - lines since the kernel will not generate them. 8192 - bytes are really enough. */ - while (__fgets_unlocked (buffer, sizeof buffer, fp) != NULL) - if (sscanf (buffer, format, &result) == 1) - { - result /= (__getpagesize () / 1024); - break; - } - - fclose (fp); + mem_unit >>= 1; + ps >>= 1; } - - if (result == -1) - /* We cannot get the needed value: signal an error. */ - __set_errno (ENOSYS); - - return result; + num *= mem_unit; + while (ps > 1) + { + ps >>= 1; + num >>= 1; + } + return num; } - -/* Return the number of pages of physical memory in the system. There - is currently (as of version 2.0.21) no system call to determine the - number. It is planned for the 2.1.x series to add this, though. - - One possibility to implement it for systems using Linux 2.0 is to - examine the pseudo file /proc/cpuinfo. Here we have one entry for - each processor. - - But not all systems have support for the /proc filesystem. If it - is not available we return -1 as an error signal. */ +/* Return the number of pages of total/available physical memory in + the system. This used to be done by parsing /proc/meminfo, but + that's unnecessarily expensive (and /proc is not always available). + The sysinfo syscall provides the same information, and has been + available at least since kernel 2.3.48. */ long int __get_phys_pages (void) { - /* XXX Here will come a test for the new system call. */ + struct sysinfo info; - return phys_pages_info ("MemTotal: %ld kB"); + __sysinfo (&info); + return sysinfo_mempages (info.totalram, info.mem_unit); } weak_alias (__get_phys_pages, get_phys_pages) - -/* Return the number of available pages of physical memory in the - system. There is currently (as of version 2.0.21) no system call - to determine the number. It is planned for the 2.1.x series to add - this, though. - - One possibility to implement it for systems using Linux 2.0 is to - examine the pseudo file /proc/cpuinfo. Here we have one entry for - each processor. - - But not all systems have support for the /proc filesystem. If it - is not available we return -1 as an error signal. */ long int __get_avphys_pages (void) { - /* XXX Here will come a test for the new system call. */ + struct sysinfo info; - return phys_pages_info ("MemFree: %ld kB"); + __sysinfo (&info); + return sysinfo_mempages (info.freeram, info.mem_unit); } weak_alias (__get_avphys_pages, get_avphys_pages) diff --git a/sysdeps/unix/sysv/linux/include/sys/sysinfo.h b/sysdeps/unix/sysv/linux/include/sys/sysinfo.h new file mode 100644 index 0000000000..45b7126b57 --- /dev/null +++ b/sysdeps/unix/sysv/linux/include/sys/sysinfo.h @@ -0,0 +1,26 @@ +/* Internal declarations for sys/sysinfo.h. + Copyright (C) 2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _INCLUDE_SYS_SYSINFO_H +#define _INCLUDE_SYS_SYSINFO_H 1 + +#include_next <sys/sysinfo.h> + +extern __typeof (sysinfo) __sysinfo __THROW; + +#endif /* sys/sysinfo.h */ diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list index ed5d38f622..62bb3cc36d 100644 --- a/sysdeps/unix/sysv/linux/syscalls.list +++ b/sysdeps/unix/sysv/linux/syscalls.list @@ -73,7 +73,7 @@ setpgid - setpgid i:ii __setpgid setpgid sigaltstack - sigaltstack i:PP __sigaltstack sigaltstack splice EXTRA splice Ci:iPiPii splice stime - stime i:p stime -sysinfo EXTRA sysinfo i:p sysinfo +sysinfo EXTRA sysinfo i:p __sysinfo sysinfo swapon - swapon i:si __swapon swapon swapoff - swapoff i:s __swapoff swapoff tee EXTRA tee Ci:iiii tee |