Skip to contentSkip to conversation

Performance improvements with scalable statistics counters

PATCH New Ravikiran G Thirumalai 1 participant 1 message Mar 1, 2002

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

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
diff -ruN linux-2.5.5/include/linux/pcpu_alloc.h statctr-2.5.5/include/linux/pcpu_alloc.h
@@ -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
diff -ruN linux-2.5.5/init/main.c statctr-2.5.5/init/main.c
@@ -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
diff -ruN linux-2.5.5/kernel/Makefile statctr-2.5.5/kernel/Makefile
@@ -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
diff -ruN linux-2.5.5/mm/Makefile statctr-2.5.5/mm/Makefile
@@ -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
diff -ruN linux-2.5.5/drivers/net/loopback.c statctrusagelo/drivers/net/loopback.c
@@ -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
diff -ruN linux-2.5.5/net/core/dev.c statctrusagelo/net/core/dev.c
@@ -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);