From patchwork Thu Aug 13 13:16:36 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rasmus Villemoes X-Patchwork-Id: 507018 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D8BE7140293 for ; Thu, 13 Aug 2015 23:17:20 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b=U4Ft/PXA; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; q=dns; s= default; b=FMVDlbSRk+ticMAt1Dk+hD9258ytgAEnZMAshhQNxtUoxN3kWb9hL TkL9wfefZCwikPP7vnZOCyNU1BhhpBpEFG6AjWk7h6ouXoEh+R/RjUTBJs3WI4dn 9cp7ntRQxPTRZgIRMA6frb6ZY92jdpTkTl49yX7CNSrHsWujgEbB18= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; s=default; bh=UT6jn9m/VA7JeUjBgNnIb/zsaTU=; b=U4Ft/PXAek6PdQcK4RZCuItg0CQT YlnlyP9xlO2Cnx/7VNazsV4sP0vU+sLNb84euydk4pWZudzfrzQnG2rVjHsZ4nxB znpLWtImUCd7JhAQGN8+rYV31+RU8YoWlUblPNP4WsV8S+3JIJqLVYCRbzaAHWaK jO4ItSA2ENUr7mc= Received: (qmail 4159 invoked by alias); 13 Aug 2015 13:17:15 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 4146 invoked by uid 89); 13 Aug 2015 13:17:13 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.4 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-lb0-f181.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=chS6PAZYpjpTnrsA4HXDxvhM9nbNYmzNEGgj6fTyDeE=; b=jWi4SMYrjW/ciPv9g2YrjOhVMh1VatD0LmIOnYqAgvTPPiV5Yja2Mk6gc6XCUmjyEA Ovg740yFqSh12JKHc8B9movHTcK1kvI2LYXb1jq09HIzQW5ggDkOUdOr+6EMTH5i2PbN o0eekpiSc8Kv05nyWY1xFeNkdtUErohvPGjWd2n5rlgl5QYJqAj9o+39FXmf7wKN/7xw eahba9msbrRQhzyON2A78hwvJWTditcXdRy/q1NcFAmdwQhXhotFZvx8b13RQI03fBbp c/EwYYjfzhAJTpHz6uaBH2yqqJuIZvDjSB0vS2AFEQnrbNJ7THuPHutRoLmknT4p0BUV a1Sg== X-Gm-Message-State: ALoCoQmjzTJlhpPzPejlz82RR9L5gPGbtVT5s4RUS+TORRuROzwbNQ8mk6QuOdrGnK6kELbQL35g X-Received: by 10.152.170.130 with SMTP id am2mr36617348lac.54.1439471827816; Thu, 13 Aug 2015 06:17:07 -0700 (PDT) From: Rasmus Villemoes To: libc-alpha@sourceware.org Cc: Linus Torvalds , Rasmus Villemoes Subject: [PATCH] getsysstats: use sysinfo() instead of parsing /proc/meminfo Date: Thu, 13 Aug 2015 15:16:36 +0200 Message-Id: <1439471796-17003-1-git-send-email-rv@rasmusvillemoes.dk> 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 #0 __get_phys_pages () at ../sysdeps/unix/sysv/linux/getsysstats.c:283 #1 0x00007ffff76d21c5 in posix_sysconf (name=) at ../sysdeps/posix/sysconf.c:634 #2 linux_sysconf (name=) at ../sysdeps/unix/sysv/linux/x86_64/../sysconf.c:136 #3 *__GI___sysconf (name=85) at ../sysdeps/unix/sysv/linux/x86_64/sysconf.c:37 #4 0x00007ffff765b02a in *(int0_t, long double) (b=, n=76, s=18446744073708915495, cmp=0x472e30, arg=0x0) at msort.c:188 #5 0x000000000042210e in ?? () #6 0x000000000042065d in main () 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 --- sysdeps/unix/sysv/linux/getsysstats.c | 88 ++++++++++++----------------------- 1 file changed, 30 insertions(+), 58 deletions(-) diff --git a/sysdeps/unix/sysv/linux/getsysstats.c b/sysdeps/unix/sysv/linux/getsysstats.c index 410f1a9..c2410cc 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)