Performance improvements with scalable statistics counters
Subsystems (3)
PATCH 1/1 Performance improvements with scalable statistics counters
Hello,
We did some measurements by modifying the loopback driver to use scalable statistics counters, and the results follow...
Recall that this approach uses per-cpu versions for counters instead of a shared global location; reducing counter cacheline bouncing thus improving SMP performance. Particularly useful for frequently updated statistics counters used in the kernel. The implementation uses a per-cpu dynamic word allocator. For the UP case, the interfaces wrap onto the usual global shared counter.
Results:
-------
1. Running tbench with 40 clients on loopback interface on 8 way smp. (Throughput in MB/sec averaged for 10 runs)
Cpus Vanilla kernel Statctr mods to lo % improvement
---- ------------- ------------------ -------------
8 72.69243 78.45725 7.93
7 71.88495 73.35581 2.04
6 77.65202 78.95159 1.67
5 83.02133 90.67332 9.21
4 83.77254 84.41258 0.76
3 73.32989 74.73163 1.91
2 56.27168 57.20956 1.67
2. Kernprof PC sample profile count in loopback_xmit (This is the lo routine which is modified to use scalable statistics counters). This was for 4 runs of tbench 40 on the loopback interface
(kernprof -b -c all -d time -f 20000 -t pc)
Cpus Vanilla Kernel Statctr mods to lo % less time spent
---- ------------- ------------------ -----------------
8 217844 66648 69.40 7 136920 65836 51.91 6 134528 64739 51.87 5 131168 62214 52.56 4 119877 65441 45.40 3 123957 63022 49.15 2 125826 59127 53.00
You can find microbenchmark comparisons and interface/implementation details on
http://lse.sourceforge.net/counters/statctr.html
Measurements above were made on a 2.4.16 kernel. Find the statctr and lo modification patches for 2.5.5 below.
Comments, suggestions welcome.
Regards,
Kiran
PS: Note that you cannot compile any other network driver if lo patch is applied too..
Statctr Patch:
--------------
include/linux/pcpu_alloc.h +75 -0
@@ -0,0 +1,75 @@ +/* +* Per-CPU Word allocator. +* +* Inspired by the freelist maintanance of the slab allocator implementation +* (in mm/slab.c) +* +* Copyright (c) International Business Machines Corp., 2001 +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Author: Ravikiran Thirumalai <[email protected]> +* +* include/linux/pcpu_alloc.h +* +*/ +#ifndef _LINUX_PCPU_ALLOC_H +#define _LINUX_PCPU_ALLOC_H + +#ifdef __KERNEL__ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/threads.h> +#include <linux/sched.h> + +/* Prototypes */ +#ifdef CONFIG_SMP + +typedef struct pcpu_ctr_s { +void *arr[NR_CPUS ];/* Pcpu counter array */ +void *blkp;/* Pointer to block from which ctr was +allocated from (for use with free code) */ +}pcpu_ctr_t ; + +extern pcpu_ctr_t *pcpu_ctr_alloc (int ); +extern void pcpu_ctr_free (pcpu_ctr_t *); +extern void __init pcpu_ctr_sys_init (void ); + +#define PCPU_CTR (ctr, cpuid) ((unsigned long *)ctr->arr[cpuid]) + +#else /* CONFIG_SMP */ + +#include <linux/slab.h> + +typedef unsigned long pcpu_ctr_t ; + +static inline pcpu_ctr_t *pcpu_ctr_alloc (int flags) +{ +return (kmalloc (sizeof (pcpu_ctr_t), flags)); +} + +static inline void pcpu_ctr_free (pcpu_ctr_t *ptr) +{ +kfree (ptr); +} + +#define pcpu_ctr_sys_init () do { } while (0) + +#endif /* CONFIG_SMP */ + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_PCPU_ALLOC_H */
include/linux/statctr.h +171 -0
171 lines not on this page · raw message
init/main.c +2 -0
@@ -27,6 +27,7 @@#include <linux/iobuf.h> #include <linux/bootmem.h> #include <linux/tty.h> +#include <linux/pcpu_alloc.h> #include <asm/io.h> #include <asm/bugs.h>
@@ -383,6 +384,7 @@* make syscalls (and thus be locked). */ smp_init (); +pcpu_ctr_sys_init ();/* Do the rest non-__init'ed, we're now alive */ rest_init ();
kernel/Makefile +2 -1
@@ -10,7 +10,7 @@ O_TARGET := kernel.o export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \ - printk.o + printk.o statctr.o obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o info.o time.o softirq.o resource.o \
@@ -20,6 +20,7 @@ obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_SMP) += statctr.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/statctr.c +104 -0
104 lines not on this page · raw message
mm/Makefile +2 -1
@@ -9,11 +9,12 @@ O_TARGET := mm.o -export-objs := shmem.o filemap.o mempool.o page_alloc.o +export-objs := shmem.o filemap.o mempool.o page_alloc.o pcpu_alloc.o obj-y := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \ page_alloc.o swap_state.o swapfile.o numa.o oom_kill.o \ shmem.o highmem.o mempool.o +obj-$(CONFIG_SMP) += pcpu_alloc.o include $(TOPDIR)/Rules.make
mm/pcpu_alloc.c +346 -0
346 lines not on this page · raw message
lo driver modifications:
------------------------
drivers/net/loopback.c +9 -5
@@ -87,10 +87,10 @@#endif dev->last_rx = jiffies; - stats->rx_bytes+=skb->len; - stats->tx_bytes+=skb->len; - stats->rx_packets++; - stats->tx_packets++; +statctr_add (&stats->rx_bytes, skb->len); +statctr_add (&stats->tx_bytes, skb->len); +statctr_inc (&stats->rx_packets); +statctr_inc (&stats->tx_packets);netif_rx (skb);
@@ -120,7 +120,11 @@ dev->priv =kmalloc (sizeof (struct net_device_stats ),GFP_KERNEL );if (dev->priv ==NULL )return -ENOMEM ; -memset (dev->priv,0 ,sizeof (struct net_device_stats )); +if (statctr_ninit ((statctr_t *)dev->priv,0 ,23 )) { +/* Init failed cleanup.. */ +kfree (dev->priv); +return -ENOMEM ; + } dev->get_stats = get_stats;/*
include/linux/netdevice.h +24 -23
65 lines not on this page · raw message
net/core/dev.c +13 -13
@@ -1691,19 +1691,19 @@if (stats) size =sprintf (buffer,"%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu %8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n" , dev->name, - stats->rx_bytes, - stats->rx_packets, stats->rx_errors, - stats->rx_dropped + stats->rx_missed_errors, - stats->rx_fifo_errors, - stats->rx_length_errors + stats->rx_over_errors - + stats->rx_crc_errors + stats->rx_frame_errors, - stats->rx_compressed, stats->multicast, - stats->tx_bytes, - stats->tx_packets, stats->tx_errors, stats->tx_dropped, - stats->tx_fifo_errors, stats->collisions, - stats->tx_carrier_errors + stats->tx_aborted_errors - + stats->tx_window_errors + stats->tx_heartbeat_errors, - stats->tx_compressed); +statctr_read (&stats->rx_bytes), +statctr_read (&stats->rx_packets),statctr_read (&stats->rx_errors), +statctr_read (&stats->rx_dropped) +statctr_read (&stats->rx_missed_errors), +statctr_read (&stats->rx_fifo_errors), +statctr_read (&stats->rx_length_errors) +statctr_read (&stats->rx_over_errors) + +statctr_read (&stats->rx_crc_errors) +statctr_read (&stats->rx_frame_errors), +statctr_read (&stats->rx_compressed),statctr_read (&stats->multicast), +statctr_read (&stats->tx_bytes), +statctr_read (&stats->tx_packets),statctr_read (&stats->tx_errors),statctr_read (&stats->tx_dropped), +statctr_read (&stats->tx_fifo_errors),statctr_read (&stats->collisions), +statctr_read (&stats->tx_carrier_errors) +statctr_read (&stats->tx_aborted_errors) + +statctr_read (&stats->tx_window_errors) +statctr_read (&stats->tx_heartbeat_errors), +statctr_read (&stats->tx_compressed));else size =sprintf (buffer,"%6s: No statistics available.\n" , dev->name);