Skip to contentSkip to conversation

Genetic Algorithm Library

RFC In review Jake Moilanen 4 participants 12 messages Jan 6, 2005 – Jan 10, 2005

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

cover Genetic Algorithm Library

I'm pleased to announce a new in-kernel library to do kernel tuning using a genetic algorithm.

This library provides hooks for kernel components to take advantage of a genetic algorithm. There are patches to hook the different schedulers included.

The basic flow of the genetic algorithm is as follows:

1.) Start w/ a broad list of initial tunable values (each set of
tunables is called a child)
2.) Let each child run for a timeslice.
3.) Once the timeslice is up, calculate the fitness of the child (how well performed).
4.) Run the next child in the list.
5.) Once all the children have run, compare the fitnesses of each child
and throw away the bottom-half performers.
6.) Create new children to take the place of the bottom-half performers
using the tunables from the top-half performers.
7.) Mutate a set number of children to keep variance.
8.) Goto step 2.

Over time the tunables should converge toward the optimal settings for that workload. If the workload changes, the tunables should converge to the new optimal settings (this is part of the reason for mutation). This algorithm is used extensively in AI.

Using these patches, there are small gains (1-3%) in Unixbench & SpecJBB. I am hoping a scheduler guru will able to rework them to give higher gains.

The main area that could use reworking is the fitness calculation. The problem is that the kernel is looking more at the micro of what's going on, instead of the macro. I am thinking of moving the fitness calculation to outside the kernel.

However, I would advocate keeping the number of layers needed to communicate between the genetic library and the hooked component down in order to keep it as lightweight as possible.

The patches are based on 2.6.9 and still a little rough, but here is the descriptions:

[1/4 genetic-lib]: This is the base patch for the genetic algorithm.

	It's based against 2.6.9.

[2/4 genetic-io-sched]: The base patch for the IO schedulers to use the
genetic library.

[3/4 genetic-as-sched]: A genetic-lib hooked anticipatory IO scheduler.

[4/4 genetic-zaphod-cpu-sched]: A hooked zaphod CPU scheduler. Depends
on the zaphod-v6 patch.

Please send comments,
Jake Moilanen

#1 lore raw

RFC 1/4 Genetic Algorithm Library

Here is the base patch for the genetic-library.

It includes generic routines for modifying and manipulating genes of children. If a specific routine is needed, that can be used instead.

---

fs/proc/proc_misc.c renamed from fs/proc/proc_misc.c~genetic-lib +30 -0
diff -puN fs/proc/proc_misc.c~genetic-lib fs/proc/proc_misc.c
@@ -45,6 +45,7 @@
 #include <linux/sysrq.h>
 #include <linux/vmalloc.h>
 #include <linux/sched_cpustats.h>
+#include <linux/genetic.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
@@ -233,6 +234,35 @@ static int meminfo_read_proc(char *page,
 	return proc_calc_metrics(page, start, off, count, eof, len);
 #undef K
 }
+
+#ifdef CONFIG_GENETIC_LIB
+extern struct proc_dir_entry * genetic_root_dir;
+
+int genetic_read_proc(char *page, char **start, off_t off,
+			  int count, int *eof, void *data)
+{
+    int i;
+    int n = 0;
+    genetic_t * genetic = (genetic_t *)data;
+    
+    n  = sprintf(page,   "generation_number:\t%ld\n", genetic->generation_number);
+    n += sprintf(page+n, "num_children:\t\t%ld\n", genetic->num_children);
+    n += sprintf(page+n, "child_number:\t\t%ld\n", genetic->child_number);
+    n += sprintf(page+n, "num_mutations:\t\t%ld\n", genetic->num_mutations);
+    n += sprintf(page+n, "avg_fitness:\t\t%ld\n", genetic->avg_fitness);
+    n += sprintf(page+n, "last_gen_avg_fitness:\t%ld\n", genetic->last_gen_avg_fitness);
+
+    n += sprintf(page+n, "\nFitness history\n");
+
+    for (i = genetic->generation_number > GENETIC_HISTORY_SIZE ? GENETIC_HISTORY_SIZE
+	     : genetic->generation_number-1; i > 0; i--)
+	n += sprintf(page+n, "generation(%ld):\t%ld\n",
+		     genetic->generation_number - i,
+		     genetic->fitness_history[(genetic->fitness_history_index - i) & GENETIC_HISTORY_MASK]);
+
+    return proc_calc_metrics(page, start, off, count, eof, n);
+}
+#endif
 
 extern struct seq_operations fragmentation_op;
 static int fragmentation_open(struct inode *inode, struct file *file)
include/linux/genetic.h new file +103 -0

103 lines not on this page · raw message

lib/Kconfig renamed from lib/Kconfig~genetic-lib +6 -0
diff -puN lib/Kconfig~genetic-lib lib/Kconfig
@@ -30,6 +30,12 @@ config LIBCRC32C
 	  require M here.  See Castagnoli93.
 	  Module will be libcrc32c.
 
+config GENETIC_LIB
+	bool "Genetic Library"
+	help
+	  This option will build in a genetic library that will tweak
+	  kernel parameters autonomically to improve performance.
+
 #
 # compression support is select'ed if needed
 #
lib/Makefile renamed from lib/Makefile~genetic-lib +1 -0
diff -puN lib/Makefile~genetic-lib lib/Makefile
@@ -18,6 +18,7 @@ endif
 obj-$(CONFIG_CRC_CCITT)	+= crc-ccitt.o
 obj-$(CONFIG_CRC32)	+= crc32.o
 obj-$(CONFIG_LIBCRC32C)	+= libcrc32c.o
+obj-$(CONFIG_GENETIC_LIB) += genetic.o
 obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
 
 obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/
lib/genetic.c new file +429 -0

429 lines not on this page · raw message

_

#4 lore raw

RFC 2/4 Genetic Algorithm Library

In reply to Jake Moilanen

This is the base patch for the io-schedulers.

It contains the fitness routine, disk_calc_fitness(), that could use a rework.

---

drivers/block/genhd.c renamed from drivers/block/genhd.c~genetic-io-sched +3 -0
diff -puN drivers/block/genhd.c~genetic-io-sched drivers/block/genhd.c
@@ -32,6 +32,8 @@ static struct blk_major_name {
 
 static spinlock_t major_names_lock = SPIN_LOCK_UNLOCKED;
 
+LIST_HEAD(gendisks);
+
 /* index in the above - for now: assume no multimajor ranges */
 static inline int major_to_index(int major)
 {
@@ -556,6 +558,7 @@ struct gendisk *alloc_disk(int minors)
 		kobj_set_kset_s(disk,block_subsys);
 		kobject_init(&disk->kobj);
 		rand_initialize_disk(disk);
+		list_add_tail(&disk->gendisks, &gendisks);
 	}
 	return disk;
 }
drivers/block/ll_rw_blk.c renamed from drivers/block/ll_rw_blk.c~genetic-io-sched +76 -1

95 lines not on this page · raw message

include/linux/genhd.h renamed from include/linux/genhd.h~genetic-io-sched +9 -0
diff -puN include/linux/genhd.h~genetic-io-sched include/linux/genhd.h
@@ -120,11 +120,20 @@ struct gendisk {
 	atomic_t sync_io;		/* RAID */
 	unsigned long stamp, stamp_idle;
 	int in_flight;
+	struct list_head gendisks;
 #ifdef	CONFIG_SMP
 	struct disk_stats *dkstats;
 #else
 	struct disk_stats dkstats;
 #endif
+#ifdef CONFIG_GENETIC_LIB
+	unsigned reads_snap;
+	unsigned writes_snap;
+	unsigned read_sectors_snap;
+	unsigned write_sectors_snap;
+	unsigned time_in_queue_snap;
+#endif	
+
 };
 
 /* 

_

#5 lore raw

RFC 3/4 Genetic Algorithm Library

In reply to Jake Moilanen

This is the hooked anticipatory IO scheduler.

Here is an example of the hooked scheduler. It should optimally tweak the tunables for the current workload.

If nothing else, hopefully the genetic-lib will help scheduler writers find the optimal tunable settings.

---

drivers/block/Kconfig.iosched renamed from drivers/block/Kconfig.iosched~genetic-as-sched +9 -0
diff -puN drivers/block/Kconfig.iosched~genetic-as-sched drivers/block/Kconfig.iosched
@@ -34,3 +34,12 @@ config IOSCHED_CFQ
 	  The CFQ I/O scheduler tries to distribute bandwidth equally
 	  among all processes in the system. It should provide a fair
 	  working environment, suitable for desktop systems.
+
+config GENETIC_IOSCHED_AS
+       bool "Genetic Anticipatory I/O scheduler (EXPERIMENTAL)" 
+       depends on IOSCHED_AS && GENETIC_LIB && EXPERIMENTAL
+       default n
+       ---help---
+       This will use a genetic algorithm to tweak the tunables of the
+       anticipatory scheduler autonomically and will adapt tunables
+       depending on the present workload.  
drivers/block/as-iosched.c renamed from drivers/block/as-iosched.c~genetic-as-sched +133 -1

191 lines not on this page · raw message

_

#6 lore raw

RFC 4/4 Genetic Algorithm Library

In reply to Jake Moilanen

The hooked zaphod CPU scheduler patch.

It depends on the zaphod-v6 patch.

The ranges for the tunables could be tweaked better.

---

kernel/sched_zaphod.c renamed from kernel/sched_zaphod.c~genetic-zaphod-cpu-sched +81 -6
diff -puN kernel/sched_zaphod.c~genetic-zaphod-cpu-sched kernel/sched_zaphod.c
@@ -21,15 +21,12 @@
  */
 #include <linux/sched.h>
 #include <linux/proc_fs.h>
+#include <linux/genetic.h>
+#include <linux/random.h>
 
 #include <asm/uaccess.h>
 
-enum zaphod_mode_enum {
-	ZAPHOD_MODE_PRIORITY_BASED,
-	ZAPHOD_MODE_ENTITLEMENT_BASED
-};
-
-static enum zaphod_mode_enum zaphod_mode = ZAPHOD_MODE_PRIORITY_BASED;
+enum zaphod_mode_enum zaphod_mode = ZAPHOD_MODE_PRIORITY_BASED;
 
 #ifdef CONFIG_SYSCTL
 static const char *zaphod_mode_names[] = {
@@ -720,3 +717,81 @@ int proc_zaphod_mode(ctl_table *ctp, int
 
 	return res;
 }
+
+#ifdef CONFIG_GENETIC_CPU_SCHED
+
+extern unsigned long sched_rr_time_slice;
+extern unsigned long base_prom_interval;
+static void zaphod_sched_create_child(genetic_child_t *);
+static void genetic_mutate_proportion(genetic_child_t *, unsigned long);
+void zaphod_sched_set_child_genes(void *);
+void zaphod_sched_calc_fitness(genetic_child_t *);
+
+struct genetic_ops zaphod_sched_genetic_ops = {
+	.create_child = zaphod_sched_create_child,
+	.set_child_genes = zaphod_sched_set_child_genes,
+	.calc_fitness = zaphod_sched_calc_fitness,
+	.combine_genes = genetic_generic_combine_genes,
+	.mutate_child = genetic_generic_mutate_child,
+};
+
+/* Each gene and their attributes */
+gene_param_t zaphod_sched_gene_param[ZAPHOD_SCHED_NUM_GENES] = {
+	{ MIN_TIMESLICE, MAX_TIMESLICE, DEF_TIMESLICE, 0 },		/* time_slice */
+	{ MIN_TIMESLICE, MAX_TIMESLICE, (100 * HZ / 1000), 0 },		/* sched_rr_time_slice */
+//	{ MIN_TIMESLICE, ULONG_MAX, ((DEF_TIMESLICE * 15) / 10), 0 },	/* base_prom_interval */
+	{ MIN_TIMESLICE, MAX_TIMESLICE, ((DEF_TIMESLICE * 15) / 10), 0 },	/* base_prom_interval */
+	{ 0, MAX_MAX_IA_BONUS, DEFAULT_MAX_IA_BONUS, 0 },		/* max_ia_bonus */
+	{ 0, MAX_MAX_IA_BONUS, 1, 0 },					/* initial_ia_bonus */
+	{ 1, 1000, DEFAULT_IA_THRESHOLD, 0 },  				/* ia_threshold */
+	{ 1, 1000, DEFAULT_CPU_HOG_THRESHOLD, 0 }, 			/* cpu_hog_threshold */
+	{ 0, MAX_MAX_TPT_BONUS, DEFAULT_MAX_TPT_BONUS, 0 },		/* max_tpt_bonus */
+	{ 1, 1000, DEFAULT_UNPRIV_RT_THRESHOLD, 0 }, 			/* unpriv_rt_threshold */
+	{ 1, 100, 1, 0 }, 						/* bgnd_time_slice_multiplier */
+	{ 0, 1, 0, 0 },							/* zaphod_mode */
+};
+
+void __init genetic_cpu_sched_init()
+{
+	if(genetic_init(0, &zaphod_sched_genetic_ops, ZAPHOD_SCHED_NUM_CHILDREN,
+			ZAPHOD_SCHED_CHILD_LIFESPAN, "zaphod-sched"))
+		panic("zaphod sched: failed to init genetic lib");
+}
+
+void zaphod_sched_create_child(genetic_child_t * child)
+{
+	int i;
+	static int child_num = 0;
+	unsigned long range;
+	int range_incr;
+	unsigned long * genes;
+	
+	BUG_ON(!child);
+	
+	child->genes = (void *)kmalloc(sizeof(struct zaphod_sched_genes), GFP_KERNEL);
+	if (!child->genes) 
+		panic("zaphod_sched_create_child: error kmalloc'n space");
+
+	child->gene_param = zaphod_sched_gene_param;
+
+	genes = (unsigned long *)child->genes;
+		
+	for (i = 0; i < ZAPHOD_SCHED_NUM_GENES; i++) {
+		range = child->gene_param[i].max - child->gene_param[i].min + 1;
+		range_incr = range / ZAPHOD_SCHED_NUM_CHILDREN;
+		if (range_incr)
+			genes[i] = child->gene_param[i].min +
+				(range_incr * child_num);
+		else
+			genes[i] = child->gene_param[i].min +
+				(child_num / (ZAPHOD_SCHED_NUM_CHILDREN / range));
+	}
+
+	child->num_genes = ZAPHOD_SCHED_NUM_GENES;
+	
+	child_num++;
+}
+
+core_initcall(genetic_cpu_sched_init);
+
+#endif /* CONFIG_GENETIC_CPU_SCHED */
lib/Kconfig renamed from lib/Kconfig~genetic-zaphod-cpu-sched +7 -0
diff -puN lib/Kconfig~genetic-zaphod-cpu-sched lib/Kconfig
@@ -36,6 +36,13 @@ config GENETIC_LIB
 	  This option will build in a genetic library that will tweak
 	  kernel parameters autonomically to improve performance.
 
+config GENETIC_CPU_SCHED
+	bool "Genetic Library - Zaphod CPU scheduler"
+	depends on GENETIC_LIB && EXPERIMENTAL && SCHEDSTATS
+	help
+	  This option will enable the genetic library on the zaphod
+	  CPU scheduler.
+
 #
 # compression support is select'ed if needed
 #
kernel/sched.c renamed from kernel/sched.c~genetic-zaphod-cpu-sched +67 -20

127 lines not on this page · raw message

include/linux/sched.h renamed from include/linux/sched.h~genetic-zaphod-cpu-sched +46 -0

72 lines not on this page · raw message

_

#2 lore raw
In reply to Jake Moilanen

On Thu, 6 Jan 2005, Jake Moilanen wrote:

Hi Jake,

fs/proc/proc_misc.c line 51

Quoted 11 lines

diff -puN fs/proc/proc_misc.c~genetic-lib fs/proc/proc_misc.c

fs/proc/proc_misc.c renamed from fs/proc/proc_misc.c~genetic-lib +1 -0
@@ -45,6 +45,7 @@
 #include <linux/sysrq.h>
 #include <linux/vmalloc.h>
 #include <linux/sched_cpustats.h>
+#include <linux/genetic.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>

This hunk fails, the line above the addition isn't in vanilla 2.6.9 - nor is the included file anywhere to be found either.

-- Cal

#3 lore raw
Quoted 2 lines · Cal Peake

This hunk fails, the line above the addition isn't in vanilla 2.6.9 - nor is the included file anywhere to be found either.

nev mind, I see this also depends on the zaphod patch. sorry for the noise.

-- Cal

#7 lore raw
In reply to Jake Moilanen

Do you have any crossover? This is critical for GA to work well - without it, the algorithm is really only a parallel random search. More specifically, is step 6 pure copies of a single parents, or can children inherit tunables from multiple parents?
- Jim

Quoted 19 lines · Jake Moilanen

...
The basic flow of the genetic algorithm is as follows:

1.) Start w/ a broad list of initial tunable values (each set of
tunables is called a child)
2.) Let each child run for a timeslice. 3.) Once the timeslice is up, calculate the fitness of the child (how well performed).
4.) Run the next child in the list.
5.) Once all the children have run, compare the fitnesses of each child
and throw away the bottom-half performers.
6.) Create new children to take the place of the bottom-half performers
using the tunables from the top-half performers.
7.) Mutate a set number of children to keep variance.
8.) Goto step 2.

Over time the tunables should converge toward the optimal settings for
that workload. If the workload changes, the tunables should converge to
the new optimal settings (this is part of the reason for mutation). This algorithm is used extensively in AI.
...

#8 lore raw

Ok I've read the patch and see you do indeed have crossover; Now I have a different question. What is the motivation for generating two children at once, instead of just one? Genes values shouldn't get "lost" since the parents are being kept around anyway. Also, since the parameters in general will not have a meaningful ordering, it might make sense for the generic crossover to be the "each gene randomly picked from one of the two parents" approach. In practice I've found that to mix things up a bit better in the parameter optimization problems I've done with GAs.
- Jim

Quoted 26 lines · James Bruce

Do you have any crossover? This is critical for GA to work well - without it, the algorithm is really only a parallel random search. More specifically, is step 6 pure copies of a single parents, or can children inherit tunables from multiple parents?
- Jim

Quoted 20 lines · Jake Moilanen

...
The basic flow of the genetic algorithm is as follows:

1.) Start w/ a broad list of initial tunable values (each set of
tunables is called a child) 2.) Let each child run for a
timeslice. 3.) Once the timeslice is up, calculate the fitness of the child (how well performed).
4.) Run the next child in the list.
5.) Once all the children have run, compare the fitnesses of each child
and throw away the bottom-half performers. 6.) Create new children
to take the place of the bottom-half performers
using the tunables from the top-half performers.
7.) Mutate a set number of children to keep variance.
8.) Goto step 2.

Over time the tunables should converge toward the optimal settings for
that workload. If the workload changes, the tunables should converge to
the new optimal settings (this is part of the reason for mutation). This algorithm is used extensively in AI.

...

#10 lore raw
In reply to Jake Moilanen

Hi

Quoted 1 line

From a quick look I've seen your algorithm tends to converge to a global

optimum, but also as William Lee Irwin III has commentend on irc, it might miss "special points" since there's no warranty of the function to minize to be continuous.

I think it's a good idea to introduce this techniques to tune the kernel, but perhaps userland would be the right place for them, to be able to switch them off when in need or have more controll over them. But it's a nice initiative in my opinion.

Regards.

Signature · 4 lines
Pedro Larroy Tovar | Linux & Network consultant |  pedro%larroy.com 
Make debian mirrors with debian-multimirror: http://pedro.larroy.com/deb_mm/
	* Las patentes de programación son nocivas para la innovación * 
				http://proinnova.hispalinux.es/
#9 lore raw
In reply to James Bruce
Quoted 9 lines · On Sat, 08 Jan 2005 09:19:42 -0500 James Bruce <[email protected]> wrote:

Ok I've read the patch and see you do indeed have crossover; Now I have a different question. What is the motivation for generating two
children at once, instead of just one? Genes values shouldn't get
"lost" since the parents are being kept around anyway. Also, since the parameters in general will not have a meaningful ordering, it might make sense for the generic crossover to be the "each gene randomly picked from one of the two parents" approach. In practice I've found that to mix things up a bit better in the parameter optimization problems I've done with GAs.

The intitial motivation for creating two children at once was so each parent could pass on all of their genes. The 75% of the parent's genes might be in child A, but the other 25% would be in child B.

Thinking about it more, there should be no reason that all of a parent's genes have to be passed on in a child. It would not be too difficult to have each gene come randomly from one of the two parents. I'll add that in on the next rev of the patches.

Thanks,
Jake

#11 lore raw
In reply to Pedro Larroy
Quoted 4 lines

From a quick look I've seen your algorithm tends to converge to a global optimum, but also as William Lee Irwin III has commentend on irc, it might miss "special points" since there's no warranty of the function to minize to be continuous.

This is a very good point, and is something that I'm working on now. I would like to be able to able to have multiple fitness rankings (ex. one that ranks specifically for throughput and one specifically for interactivity/latency). Then tune specific genes, that actually impact that specific fitness check.

Quoted 4 lines

I think it's a good idea to introduce this techniques to tune the kernel, but perhaps userland would be the right place for them, to be able to switch them off when in need or have more controll over them. But it's a nice initiative in my opinion.

I considered doing this in userland at first, but I went away from it for a couple reasons. I wanted users of the library to have a lot of flexibility. There was also a concern with the extra overhead going inbetween user/kernel space (important for users who's children have very short life-spans).

Jake