読者です 読者をやめる 読者になる 読者になる

φ(・・*)ゞ ウーン kmalloc()使用箇所をスラブ・キャッシュを使うように変えたらどうなるか

kernel linux

Linuxカーネルのfork.cにあるcopy_process()を見ててkmalloc()を使っている箇所をkmem_cache_alloc()を使うようにしたらどうなんだろ?とふと思ったので試してみました。試したのはauditsc.cにあるaudit_alloc_context()です。この関数はcopy_process()から呼ばれるのでfork(2)、clone(2)が呼ばれると実行されるやつです。
カーネルはgitのv3.8タグのやつをチェックアウトして弄りました。

実行環境は仮想マシンでメモリが4GiB、cpuは以下のとおりです。

[masami@kerntest:~]$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 2
model name      : QEMU Virtual CPU version 1.2.2
stepping        : 3
microcode       : 0x1
cpu MHz         : 3103.054
cache size      : 4096 KB
fpu             : yes
fpu_exception   : yes
cpuid level     : 4
wp              : yes
flags           : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pse3
6 clflush mmx fxsr sse sse2 syscall nx lm rep_good nopl pni cx16 popcnt hypervis
or lahf_lm
bogomips        : 6206.10
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 2
model name      : QEMU Virtual CPU version 1.2.2
stepping        : 3
microcode       : 0x1
cpu MHz         : 3103.054
cache size      : 4096 KB
fpu             : yes
fpu_exception   : yes
cpuid level     : 4
wp              : yes
flags           : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pse3
6 clflush mmx fxsr sse sse2 syscall nx lm rep_good nopl pni cx16 popcnt hypervis
or lahf_lm
bogomips        : 6206.10
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

カーネルはこのように変更しました。

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 5a6d718..b90bf0b 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -89,6 +89,7 @@ struct filename;
 #ifdef CONFIG_AUDITSYSCALL
 /* These are defined in auditsc.c */
 				/* Public API */
+extern int __init audit_cache_init(void);
 extern int  audit_alloc(struct task_struct *task);
 extern void __audit_free(struct task_struct *task);
 extern void __audit_syscall_entry(int arch,
@@ -281,6 +282,10 @@ static inline void audit_mmap_fd(int fd, int flags)
 extern int audit_n_rules;
 extern int audit_signals;
 #else /* CONFIG_AUDITSYSCALL */
+static inline int __init audit_cache_init(void)
+{
+	return 0;
+}
 static inline int audit_alloc(struct task_struct *task)
 {
 	return 0;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index a371f85..7f51229 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -275,6 +275,8 @@ struct audit_context {
 #endif
 };
 
+static struct kmem_cache *audit_cachep;
+
 static inline int open_arg(int flags, int mask)
 {
 	int n = ACC_MODE(flags);
@@ -1046,8 +1048,9 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
 {
 	struct audit_context *context;
 
-	if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
+	if (!(context = kmem_cache_alloc(audit_cachep, GFP_KERNEL)))
 		return NULL;
+
 	audit_zero_context(context, state);
 	INIT_LIST_HEAD(&context->killed_trees);
 	INIT_LIST_HEAD(&context->names_list);
@@ -1096,7 +1099,7 @@ static inline void audit_free_context(struct audit_context *context)
 	audit_free_aux(context);
 	kfree(context->filterkey);
 	kfree(context->sockaddr);
-	kfree(context);
+	kmem_cache_free(audit_cachep, context);
 }
 
 void audit_log_task_context(struct audit_buffer *ab)
@@ -2749,3 +2752,11 @@ struct list_head *audit_killed_trees(void)
 		return NULL;
 	return &ctx->killed_trees;
 }
+
+int __init audit_cache_init(void)
+{
+	printk(KERN_INFO"use slab cache instead of kmalloc in audit_alloc_context()\n");
+	audit_cachep = KMEM_CACHE(audit_context, SLAB_PANIC);
+	return 0;
+}
+
diff --git a/kernel/fork.c b/kernel/fork.c
index c535f33..f631aa4 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1715,6 +1715,7 @@ void __init proc_caches_init(void)
 	vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC);
 	mmap_init();
 	nsproxy_cache_init();
+	audit_cache_init();
 }
 
 /*

次にhackbenchをダウンロードしてコンパイルします。

$ gcc hackbench.c -o hackbench -lpthread

そうしましたら、以下のようなシェルを走らせてログをとります。hackbenchは改造前のv3.8でも実行してます。

#!/bin/bash

for i in {0..30};
do
    echo "Test $i"
    ./hackbench -pipe 20 process 40
done

これが結果です。

No. kmallo() kmem_cache_alloc()
1 0.141 0.151
2 0.128 0.121
3 0.156 0.14
4 0.142 0.12
5 0.138 0.147
6 0.123 0.128
7 0.146 0.129
8 0.122 0.126
9 0.128 0.138
10 0.124 0.141
11 0.12 0.139
12 0.122 0.156
13 0.113 0.137
14 0.129 0.132
15 0.176 0.135
16 0.122 0.163
17 0.148 0.129
18 0.133 0.136
19 0.125 0.128
20 0.143 0.147
21 0.129 0.149
22 0.123 0.13
23 0.148 0.128
24 0.12 0.133
25 0.126 0.122
26 0.141 0.124
27 0.133 0.125
28 0.134 0.141
29 0.136 0.162
30 0.135 0.128
31 0.126 0.127
平均 0.133225806451613 0.135870967741935

グラフだとこのように。
f:id:masami256:20130312231053p:plain

(´-`).。oO(一回くらい実機で試してみるかなぁ。