procfs: add seq_put_hex_ll to speed up /proc/pid/maps
authorAndrei Vagin <avagin@openvz.org>
Tue, 10 Apr 2018 23:30:44 +0000 (16:30 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 11 Apr 2018 17:28:32 +0000 (10:28 -0700)
seq_put_hex_ll() prints a number in hexadecimal notation and works
faster than seq_printf().

== test.py
  num = 0
  with open("/proc/1/maps") as f:
          while num < 10000 :
                  data = f.read()
                  f.seek(0, 0)
                 num = num + 1
==

== Before patch ==
  $  time python test.py

  real 0m1.561s
  user 0m0.257s
  sys 0m1.302s

== After patch ==
  $ time python test.py

  real 0m0.986s
  user 0m0.279s
  sys 0m0.707s

$ perf -g record python test.py:

== Before patch ==
-   67.42%     2.82%  python   [kernel.kallsyms] [k] show_map_vma.isra.22
   - 64.60% show_map_vma.isra.22
      - 44.98% seq_printf
         - seq_vprintf
            - vsnprintf
               + 14.85% number
               + 12.22% format_decode
                 5.56% memcpy_erms
      + 15.06% seq_path
      + 4.42% seq_pad
   + 2.45% __GI___libc_read

== After patch ==
-   47.35%     3.38%  python   [kernel.kallsyms] [k] show_map_vma.isra.23
   - 43.97% show_map_vma.isra.23
      + 20.84% seq_path
      - 15.73% show_vma_header_prefix
           10.55% seq_put_hex_ll
         + 2.65% seq_put_decimal_ull
           0.95% seq_putc
      + 6.96% seq_pad
   + 2.94% __GI___libc_read

[avagin@openvz.org: use unsigned int instead of int where it is suitable]
Link: http://lkml.kernel.org/r/20180214025619.4005-1-avagin@openvz.org
[avagin@openvz.org: v2]
Link: http://lkml.kernel.org/r/20180117082050.25406-1-avagin@openvz.org
Link: http://lkml.kernel.org/r/20180112185812.7710-1-avagin@openvz.org
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/proc/task_mmu.c
fs/seq_file.c
include/linux/seq_file.h

index ec6d2983a5cb65e5c46a40c5b6a18da11db10749..b66fc8de7d34a3467d1badceaec452220454bf54 100644 (file)
@@ -287,15 +287,18 @@ static void show_vma_header_prefix(struct seq_file *m,
                                   dev_t dev, unsigned long ino)
 {
        seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
-       seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
-                  start,
-                  end,
-                  flags & VM_READ ? 'r' : '-',
-                  flags & VM_WRITE ? 'w' : '-',
-                  flags & VM_EXEC ? 'x' : '-',
-                  flags & VM_MAYSHARE ? 's' : 'p',
-                  pgoff,
-                  MAJOR(dev), MINOR(dev), ino);
+       seq_put_hex_ll(m, NULL, start, 8);
+       seq_put_hex_ll(m, "-", end, 8);
+       seq_putc(m, ' ');
+       seq_putc(m, flags & VM_READ ? 'r' : '-');
+       seq_putc(m, flags & VM_WRITE ? 'w' : '-');
+       seq_putc(m, flags & VM_EXEC ? 'x' : '-');
+       seq_putc(m, flags & VM_MAYSHARE ? 's' : 'p');
+       seq_put_hex_ll(m, " ", pgoff, 8);
+       seq_put_hex_ll(m, " ", MAJOR(dev), 2);
+       seq_put_hex_ll(m, ":", MINOR(dev), 2);
+       seq_put_decimal_ull(m, " ", ino);
+       seq_putc(m, ' ');
 }
 
 static void
index eea09f6d883056b8e6fcc0aad857ab1a76fdb3f1..cde1bdbf7801c4d0af321f0a160849ffdbe1561b 100644 (file)
@@ -715,6 +715,52 @@ overflow:
 }
 EXPORT_SYMBOL(seq_put_decimal_ull);
 
+/**
+ * seq_put_hex_ll - put a number in hexadecimal notation
+ * @m: seq_file identifying the buffer to which data should be written
+ * @delimiter: a string which is printed before the number
+ * @v: the number
+ * @width: a minimum field width
+ *
+ * seq_put_hex_ll(m, "", v, 8) is equal to seq_printf(m, "%08llx", v)
+ *
+ * This routine is very quick when you show lots of numbers.
+ * In usual cases, it will be better to use seq_printf(). It's easier to read.
+ */
+void seq_put_hex_ll(struct seq_file *m, const char *delimiter,
+                               unsigned long long v, unsigned int width)
+{
+       unsigned int len;
+       int i;
+
+       if (delimiter && delimiter[0]) {
+               if (delimiter[1] == 0)
+                       seq_putc(m, delimiter[0]);
+               else
+                       seq_puts(m, delimiter);
+       }
+
+       /* If x is 0, the result of __builtin_clzll is undefined */
+       if (v == 0)
+               len = 1;
+       else
+               len = (sizeof(v) * 8 - __builtin_clzll(v) + 3) / 4;
+
+       if (len < width)
+               len = width;
+
+       if (m->count + len > m->size) {
+               seq_set_overflow(m);
+               return;
+       }
+
+       for (i = len - 1; i >= 0; i--) {
+               m->buf[m->count + i] = hex_asc[0xf & v];
+               v = v >> 4;
+       }
+       m->count += len;
+}
+
 void seq_put_decimal_ll(struct seq_file *m, const char *delimiter, long long num)
 {
        int len;
index ab437dd2e3b9a5dc222824cb82eeda86124f614e..599e145f49175964fedda1d9afe2f43574904963 100644 (file)
@@ -121,6 +121,9 @@ void seq_puts(struct seq_file *m, const char *s);
 void seq_put_decimal_ull(struct seq_file *m, const char *delimiter,
                         unsigned long long num);
 void seq_put_decimal_ll(struct seq_file *m, const char *delimiter, long long num);
+void seq_put_hex_ll(struct seq_file *m, const char *delimiter,
+                   unsigned long long v, unsigned int width);
+
 void seq_escape(struct seq_file *m, const char *s, const char *esc);
 
 void seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,