diff options
author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2012-03-23 18:02:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-23 19:58:42 -0400 |
commit | 1ac101a5d675aca2426c5cd460c73fb95acb8391 (patch) | |
tree | 5d993fde0c5e67de97c0d9ffac54163f06fc90c9 | |
parent | 59a32e2ce5eb809967cac4e718bc527beca83c59 (diff) |
procfs: add num_to_str() to speed up /proc/stat
== stat_check.py
num = 0
with open("/proc/stat") as f:
while num < 1000 :
data = f.read()
f.seek(0, 0)
num = num + 1
==
perf shows
20.39% stat_check.py [kernel.kallsyms] [k] format_decode
13.41% stat_check.py [kernel.kallsyms] [k] number
12.61% stat_check.py [kernel.kallsyms] [k] vsnprintf
10.85% stat_check.py [kernel.kallsyms] [k] memcpy
4.85% stat_check.py [kernel.kallsyms] [k] radix_tree_lookup
4.43% stat_check.py [kernel.kallsyms] [k] seq_printf
This patch removes most of calls to vsnprintf() by adding num_to_str()
and seq_print_decimal_ull(), which prints decimal numbers without rich
functions provided by printf().
On my 8cpu box.
== Before patch ==
[root@bluextal test]# time ./stat_check.py
real 0m0.150s
user 0m0.026s
sys 0m0.121s
== After patch ==
[root@bluextal test]# time ./stat_check.py
real 0m0.055s
user 0m0.022s
sys 0m0.030s
[akpm@linux-foundation.org: remove incorrect comment, use less statck in num_to_str(), move comment from .h to .c, simplify seq_put_decimal_ull()]
[andrea@betterlinux.com: avoid breaking the ABI in /proc/stat]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrea Righi <andrea@betterlinux.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Glauber Costa <glommer@parallels.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Turner <pjt@google.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/proc/stat.c | 55 | ||||
-rw-r--r-- | fs/seq_file.c | 33 | ||||
-rw-r--r-- | include/linux/kernel.h | 2 | ||||
-rw-r--r-- | include/linux/seq_file.h | 3 | ||||
-rw-r--r-- | lib/vsprintf.c | 20 |
5 files changed, 84 insertions, 29 deletions
diff --git a/fs/proc/stat.c b/fs/proc/stat.c index ac446114cd48..6a0c62d6e442 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c | |||
@@ -89,18 +89,19 @@ static int show_stat(struct seq_file *p, void *v) | |||
89 | } | 89 | } |
90 | sum += arch_irq_stat(); | 90 | sum += arch_irq_stat(); |
91 | 91 | ||
92 | seq_printf(p, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu " | 92 | seq_puts(p, "cpu "); |
93 | "%llu\n", | 93 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user)); |
94 | (unsigned long long)cputime64_to_clock_t(user), | 94 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice)); |
95 | (unsigned long long)cputime64_to_clock_t(nice), | 95 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system)); |
96 | (unsigned long long)cputime64_to_clock_t(system), | 96 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle)); |
97 | (unsigned long long)cputime64_to_clock_t(idle), | 97 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait)); |
98 | (unsigned long long)cputime64_to_clock_t(iowait), | 98 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq)); |
99 | (unsigned long long)cputime64_to_clock_t(irq), | 99 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq)); |
100 | (unsigned long long)cputime64_to_clock_t(softirq), | 100 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal)); |
101 | (unsigned long long)cputime64_to_clock_t(steal), | 101 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest)); |
102 | (unsigned long long)cputime64_to_clock_t(guest), | 102 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice)); |
103 | (unsigned long long)cputime64_to_clock_t(guest_nice)); | 103 | seq_putc(p, '\n'); |
104 | |||
104 | for_each_online_cpu(i) { | 105 | for_each_online_cpu(i) { |
105 | /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ | 106 | /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ |
106 | user = kcpustat_cpu(i).cpustat[CPUTIME_USER]; | 107 | user = kcpustat_cpu(i).cpustat[CPUTIME_USER]; |
@@ -113,26 +114,24 @@ static int show_stat(struct seq_file *p, void *v) | |||
113 | steal = kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; | 114 | steal = kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; |
114 | guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; | 115 | guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; |
115 | guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; | 116 | guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; |
116 | seq_printf(p, | 117 | seq_printf(p, "cpu%d", i); |
117 | "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu " | 118 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user)); |
118 | "%llu\n", | 119 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice)); |
119 | i, | 120 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system)); |
120 | (unsigned long long)cputime64_to_clock_t(user), | 121 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle)); |
121 | (unsigned long long)cputime64_to_clock_t(nice), | 122 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait)); |
122 | (unsigned long long)cputime64_to_clock_t(system), | 123 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq)); |
123 | (unsigned long long)cputime64_to_clock_t(idle), | 124 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq)); |
124 | (unsigned long long)cputime64_to_clock_t(iowait), | 125 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal)); |
125 | (unsigned long long)cputime64_to_clock_t(irq), | 126 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest)); |
126 | (unsigned long long)cputime64_to_clock_t(softirq), | 127 | seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice)); |
127 | (unsigned long long)cputime64_to_clock_t(steal), | 128 | seq_putc(p, '\n'); |
128 | (unsigned long long)cputime64_to_clock_t(guest), | ||
129 | (unsigned long long)cputime64_to_clock_t(guest_nice)); | ||
130 | } | 129 | } |
131 | seq_printf(p, "intr %llu", (unsigned long long)sum); | 130 | seq_printf(p, "intr %llu", (unsigned long long)sum); |
132 | 131 | ||
133 | /* sum again ? it could be updated? */ | 132 | /* sum again ? it could be updated? */ |
134 | for_each_irq_nr(j) | 133 | for_each_irq_nr(j) |
135 | seq_printf(p, " %u", kstat_irqs(j)); | 134 | seq_put_decimal_ull(p, ' ', kstat_irqs(j)); |
136 | 135 | ||
137 | seq_printf(p, | 136 | seq_printf(p, |
138 | "\nctxt %llu\n" | 137 | "\nctxt %llu\n" |
@@ -149,7 +148,7 @@ static int show_stat(struct seq_file *p, void *v) | |||
149 | seq_printf(p, "softirq %llu", (unsigned long long)sum_softirq); | 148 | seq_printf(p, "softirq %llu", (unsigned long long)sum_softirq); |
150 | 149 | ||
151 | for (i = 0; i < NR_SOFTIRQS; i++) | 150 | for (i = 0; i < NR_SOFTIRQS; i++) |
152 | seq_printf(p, " %u", per_softirq_sums[i]); | 151 | seq_put_decimal_ull(p, ' ', per_softirq_sums[i]); |
153 | seq_putc(p, '\n'); | 152 | seq_putc(p, '\n'); |
154 | 153 | ||
155 | return 0; | 154 | return 0; |
diff --git a/fs/seq_file.c b/fs/seq_file.c index aa242dc99373..7d19816c4cc9 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c | |||
@@ -644,6 +644,39 @@ int seq_puts(struct seq_file *m, const char *s) | |||
644 | } | 644 | } |
645 | EXPORT_SYMBOL(seq_puts); | 645 | EXPORT_SYMBOL(seq_puts); |
646 | 646 | ||
647 | /* | ||
648 | * A helper routine for putting decimal numbers without rich format of printf(). | ||
649 | * only 'unsigned long long' is supported. | ||
650 | * This routine will put one byte delimiter + number into seq_file. | ||
651 | * This routine is very quick when you show lots of numbers. | ||
652 | * In usual cases, it will be better to use seq_printf(). It's easier to read. | ||
653 | */ | ||
654 | int seq_put_decimal_ull(struct seq_file *m, char delimiter, | ||
655 | unsigned long long num) | ||
656 | { | ||
657 | int len; | ||
658 | |||
659 | if (m->count + 2 >= m->size) /* we'll write 2 bytes at least */ | ||
660 | goto overflow; | ||
661 | |||
662 | m->buf[m->count++] = delimiter; | ||
663 | |||
664 | if (num < 10) { | ||
665 | m->buf[m->count++] = num + '0'; | ||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | len = num_to_str(m->buf + m->count, m->size - m->count, num); | ||
670 | if (!len) | ||
671 | goto overflow; | ||
672 | m->count += len; | ||
673 | return 0; | ||
674 | overflow: | ||
675 | m->count = m->size; | ||
676 | return -1; | ||
677 | } | ||
678 | EXPORT_SYMBOL(seq_put_decimal_ull); | ||
679 | |||
647 | /** | 680 | /** |
648 | * seq_write - write arbitrary data to buffer | 681 | * seq_write - write arbitrary data to buffer |
649 | * @seq: seq_file identifying the buffer to which data should be written | 682 | * @seq: seq_file identifying the buffer to which data should be written |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index f2085b541a24..3e140add5360 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
@@ -312,6 +312,8 @@ extern long long simple_strtoll(const char *,char **,unsigned int); | |||
312 | #define strict_strtoull kstrtoull | 312 | #define strict_strtoull kstrtoull |
313 | #define strict_strtoll kstrtoll | 313 | #define strict_strtoll kstrtoll |
314 | 314 | ||
315 | extern int num_to_str(char *buf, int size, unsigned long long num); | ||
316 | |||
315 | /* lib/printf utilities */ | 317 | /* lib/printf utilities */ |
316 | 318 | ||
317 | extern __printf(2, 3) int sprintf(char *buf, const char * fmt, ...); | 319 | extern __printf(2, 3) int sprintf(char *buf, const char * fmt, ...); |
diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 44f1514b00ba..5bba42c99448 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h | |||
@@ -121,9 +121,10 @@ int single_release(struct inode *, struct file *); | |||
121 | void *__seq_open_private(struct file *, const struct seq_operations *, int); | 121 | void *__seq_open_private(struct file *, const struct seq_operations *, int); |
122 | int seq_open_private(struct file *, const struct seq_operations *, int); | 122 | int seq_open_private(struct file *, const struct seq_operations *, int); |
123 | int seq_release_private(struct inode *, struct file *); | 123 | int seq_release_private(struct inode *, struct file *); |
124 | int seq_put_decimal_ull(struct seq_file *m, char delimiter, | ||
125 | unsigned long long num); | ||
124 | 126 | ||
125 | #define SEQ_START_TOKEN ((void *)1) | 127 | #define SEQ_START_TOKEN ((void *)1) |
126 | |||
127 | /* | 128 | /* |
128 | * Helpers for iteration over list_head-s in seq_files | 129 | * Helpers for iteration over list_head-s in seq_files |
129 | */ | 130 | */ |
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 38e612e66da5..385c40291cdb 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c | |||
@@ -212,6 +212,26 @@ char *put_dec(char *buf, unsigned long long num) | |||
212 | } | 212 | } |
213 | } | 213 | } |
214 | 214 | ||
215 | /* | ||
216 | * Convert passed number to decimal string. | ||
217 | * Returns the length of string. On buffer overflow, returns 0. | ||
218 | * | ||
219 | * If speed is not important, use snprintf(). It's easy to read the code. | ||
220 | */ | ||
221 | int num_to_str(char *buf, int size, unsigned long long num) | ||
222 | { | ||
223 | char tmp[21]; /* Enough for 2^64 in decimal */ | ||
224 | int idx, len; | ||
225 | |||
226 | len = put_dec(tmp, num) - tmp; | ||
227 | |||
228 | if (len > size) | ||
229 | return 0; | ||
230 | for (idx = 0; idx < len; ++idx) | ||
231 | buf[idx] = tmp[len - idx - 1]; | ||
232 | return len; | ||
233 | } | ||
234 | |||
215 | #define ZEROPAD 1 /* pad with zero */ | 235 | #define ZEROPAD 1 /* pad with zero */ |
216 | #define SIGN 2 /* unsigned/signed long */ | 236 | #define SIGN 2 /* unsigned/signed long */ |
217 | #define PLUS 4 /* show plus */ | 237 | #define PLUS 4 /* show plus */ |