Genetic Algorithm Library
Subsystems (4)
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
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
@@ -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
@@ -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
@@ -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
_
RFC 2/4 Genetic Algorithm Library
In reply to Jake MoilanenThis 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
@@ -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
@@ -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 + };/*
_
RFC 3/4 Genetic Algorithm Library
In reply to Jake MoilanenThis 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
@@ -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
_
RFC 4/4 Genetic Algorithm Library
In reply to Jake MoilanenThe 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
@@ -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, intreturn 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
@@ -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
_
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-libfs/proc/proc_misc.cfs/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
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
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.
...
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?
- JimQuoted 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....
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/
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
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