From d99a29d2174ea510d3a9fe8c6334a4ff6725f71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Dejean?= Date: Sun, 3 May 2009 16:40:13 +0200 Subject: [PATCH] Looks for smaps member using a gperf table instead of using bsearch. Brings a 25% boost. --- sysdeps/linux/Makefile.am | 2 + sysdeps/linux/procmap.c | 65 ++++------- sysdeps/linux/procmap_smaps.c | 172 ++++++++++++++++++++++++++++++ sysdeps/linux/procmap_smaps.gperf | 21 ++++ 4 files changed, 218 insertions(+), 42 deletions(-) create mode 100644 sysdeps/linux/procmap_smaps.c create mode 100644 sysdeps/linux/procmap_smaps.gperf diff --git a/sysdeps/linux/Makefile.am b/sysdeps/linux/Makefile.am index ca3c471d..7307ba54 100644 --- a/sysdeps/linux/Makefile.am +++ b/sysdeps/linux/Makefile.am @@ -2,6 +2,8 @@ INCLUDES = @INCLUDES@ noinst_LTLIBRARIES = libgtop_sysdeps-2.0.la +EXTRA_DIST = procmap_smaps.gperf procmap_smaps.c + libgtop_sysdeps_2_0_la_SOURCES = open.c close.c cpu.c mem.c swap.c \ uptime.c loadavg.c shm_limits.c msg_limits.c \ sem_limits.c proclist.c procstate.c procuid.c \ diff --git a/sysdeps/linux/procmap.c b/sysdeps/linux/procmap.c index 40efafca..31dd9b13 100644 --- a/sysdeps/linux/procmap.c +++ b/sysdeps/linux/procmap.c @@ -31,6 +31,7 @@ #include "glibtop_private.h" +#include "procmap_smaps.c" #define MAPS_FILE "/proc/%u/maps" #define SMAPS_FILE "/proc/%u/smaps" @@ -66,35 +67,19 @@ _glibtop_init_proc_map_s (glibtop *server) /* Provides detailed information about a process. */ - -struct smap_value { - char name[16]; - size_t name_len; - ptrdiff_t offset; -}; - -static int -compare(const void* a_key, const void* a_smap) -{ - const char* key = a_key; - const struct smap_value* smap = a_smap; - return strncmp(key, smap->name, smap->name_len); -} - - -static gboolean +static const char* is_smap_value(const char* s) { for ( ; *s; ++s) { if (isspace(*s)) - return FALSE; + return NULL; if (*s == ':') - return TRUE; + return s; } - return FALSE; + return NULL; } @@ -105,25 +90,18 @@ is_smap_value(const char* s) static gboolean parse_smaps(glibtop_map_entry *entry, const char* line) { -#define SMAP_OFFSET(MEMBER) offsetof(glibtop_map_entry, MEMBER) -#define STR_AND_LEN(X) (X), (sizeof X - 1) - /* keep sorted */ - const struct smap_value values[] = { - { STR_AND_LEN("Private_Clean:"), SMAP_OFFSET(private_clean) }, - { STR_AND_LEN("Private_Dirty:"), SMAP_OFFSET(private_dirty) }, - { STR_AND_LEN("Rss:"), SMAP_OFFSET(rss) }, - { STR_AND_LEN("Shared_Clean:"), SMAP_OFFSET(shared_clean) }, - { STR_AND_LEN("Shared_Dirty:"), SMAP_OFFSET(shared_dirty) }, - { STR_AND_LEN("Size:"), SMAP_OFFSET(size) } - }; + const struct smap_value* smap; + size_t len; + const char* colon; -#undef STR_AND_LEN -#undef SMAP_OFFSET + if ((colon = is_smap_value(line)) == NULL) + return FALSE; - struct smap_value* smap; + len = colon - line + 1; + smap = _glibtop_find_smap(line, len); - smap = bsearch(line, values, G_N_ELEMENTS(values), sizeof values[0], compare); +// g_debug("smap %s -> %p", line, smap); if (smap) { char *offset; @@ -133,11 +111,10 @@ parse_smaps(glibtop_map_entry *entry, const char* line) offset += smap->offset; value = (void*) offset; - *value = get_scaled(line + smap->name_len, NULL); - return TRUE; + *value = get_scaled(line + len, NULL); } - return is_smap_value(line); + return TRUE; } @@ -215,6 +192,7 @@ glibtop_get_proc_map_s (glibtop *server, glibtop_proc_map *buf, pid_t pid) It's the average number of entry per process on my laptop */ + size_t added = 0; GArray *entry_list = g_array_sized_new(FALSE, FALSE, sizeof(glibtop_map_entry), 100); @@ -242,7 +220,6 @@ glibtop_get_proc_map_s (glibtop *server, glibtop_proc_map *buf, pid_t pid) while(TRUE) { unsigned long perm; - guint len; /* int line_end; */ unsigned short dev_major, dev_minor; @@ -293,9 +270,12 @@ glibtop_get_proc_map_s (glibtop *server, glibtop_proc_map *buf, pid_t pid) avoid copying the entry, grow by 1 and point to the last element. */ - len = entry_list->len; - g_array_set_size(entry_list, len + 1); - entry = &g_array_index(entry_list, glibtop_map_entry, len); + + if (G_UNLIKELY(added >= entry_list->len)) { + g_array_set_size(entry_list, 2 * entry_list->len); + } + + entry = &g_array_index(entry_list, glibtop_map_entry, added++); entry->flags = _glibtop_sysdeps_map_entry; entry->start = start; @@ -322,6 +302,7 @@ glibtop_get_proc_map_s (glibtop *server, glibtop_proc_map *buf, pid_t pid) eof: + g_array_set_size(entry_list, added); free(line); fclose (maps); diff --git a/sysdeps/linux/procmap_smaps.c b/sysdeps/linux/procmap_smaps.c new file mode 100644 index 00000000..b7119a23 --- /dev/null +++ b/sysdeps/linux/procmap_smaps.c @@ -0,0 +1,172 @@ +/* ANSI-C code produced by gperf version 3.0.3 */ +/* Command-line: gperf sysdeps/linux/procmap_smaps.gperf */ +/* Computed positions: -k'9' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +#line 8 "sysdeps/linux/procmap_smaps.gperf" + +#include "glibtop_private.h" +#include +#include +#define SMAP_OFFSET(MEMBER) offsetof(glibtop_map_entry, MEMBER) +#line 14 "sysdeps/linux/procmap_smaps.gperf" +struct smap_value { int name; ptrdiff_t offset; }; +#include + +#define TOTAL_KEYWORDS 6 +#define MIN_WORD_LENGTH 4 +#define MAX_WORD_LENGTH 14 +#define MIN_HASH_VALUE 4 +#define MAX_HASH_VALUE 19 +/* maximum key range = 16, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 5, 0, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 5, 20, 20, 0, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[8]]; + /*FALLTHROUGH*/ + case 8: + case 7: + case 6: + case 5: + case 4: + break; + } + return hval; +} + +struct stringpool_t + { + char stringpool_str4[sizeof("Rss:")]; + char stringpool_str5[sizeof("Size:")]; + char stringpool_str13[sizeof("Shared_Clean:")]; + char stringpool_str14[sizeof("Private_Dirty:")]; + char stringpool_str18[sizeof("Shared_Dirty:")]; + char stringpool_str19[sizeof("Private_Clean:")]; + }; +static const struct stringpool_t stringpool_contents = + { + "Rss:", + "Size:", + "Shared_Clean:", + "Private_Dirty:", + "Shared_Dirty:", + "Private_Clean:" + }; +#define stringpool ((const char *) &stringpool_contents) +#ifdef __GNUC__ +__inline +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +static /* manually added */ +const struct smap_value * +_glibtop_find_smap (register const char *str, register unsigned int len) +{ + static const unsigned char lengthtable[] = + { + 0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 13, + 14, 0, 0, 0, 13, 14 + }; + static const struct smap_value wordlist[] = + { + {-1}, {-1}, {-1}, {-1}, +#line 18 "sysdeps/linux/procmap_smaps.gperf" + {(int)(long)&((struct stringpool_t *)0)->stringpool_str4, SMAP_OFFSET(rss)}, +#line 21 "sysdeps/linux/procmap_smaps.gperf" + {(int)(long)&((struct stringpool_t *)0)->stringpool_str5, SMAP_OFFSET(size)}, + {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, +#line 19 "sysdeps/linux/procmap_smaps.gperf" + {(int)(long)&((struct stringpool_t *)0)->stringpool_str13, SMAP_OFFSET(shared_clean)}, +#line 17 "sysdeps/linux/procmap_smaps.gperf" + {(int)(long)&((struct stringpool_t *)0)->stringpool_str14, SMAP_OFFSET(private_dirty)}, + {-1}, {-1}, {-1}, +#line 20 "sysdeps/linux/procmap_smaps.gperf" + {(int)(long)&((struct stringpool_t *)0)->stringpool_str18, SMAP_OFFSET(shared_dirty)}, +#line 16 "sysdeps/linux/procmap_smaps.gperf" + {(int)(long)&((struct stringpool_t *)0)->stringpool_str19, SMAP_OFFSET(private_clean)} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + if (len == lengthtable[key]) + { + register const char *s = wordlist[key].name + stringpool; + + if (*str == *s && !memcmp (str + 1, s + 1, len - 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/sysdeps/linux/procmap_smaps.gperf b/sysdeps/linux/procmap_smaps.gperf new file mode 100644 index 00000000..98a7df09 --- /dev/null +++ b/sysdeps/linux/procmap_smaps.gperf @@ -0,0 +1,21 @@ +%language=ANSI-C +%includes +%struct-type +%readonly-tables +%pic +%define lookup-function-name _glibtop_find_smap +%compare-lengths +%{ +#include "glibtop_private.h" +#include +#include +#define SMAP_OFFSET(MEMBER) offsetof(glibtop_map_entry, MEMBER) +%} +struct smap_value { int name; ptrdiff_t offset; }; +%% +Private_Clean:, SMAP_OFFSET(private_clean) +Private_Dirty:, SMAP_OFFSET(private_dirty) +Rss:, SMAP_OFFSET(rss) +Shared_Clean:, SMAP_OFFSET(shared_clean) +Shared_Dirty:, SMAP_OFFSET(shared_dirty) +Size:, SMAP_OFFSET(size)