Change Log 0.1 1/7/2004 - Added ability for mutates to take iterative steps. - Moved debug info to a /proc file. --- diff -puN fs/proc/proc_misc.c~genetic-lib fs/proc/proc_misc.c --- linux-2.6.10/fs/proc/proc_misc.c~genetic-lib Mon Jan 10 17:09:54 2005 +++ linux-2.6.10-moilanen/fs/proc/proc_misc.c Mon Jan 10 17:43:22 2005 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -238,6 +239,64 @@ 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); +} + +#if GENETIC_DEBUG +int genetic_debug_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int i, j, k; + int n = 0; + genetic_t * genetic = (genetic_t *)data; + + n = sprintf(page, "generation_number:\t%ld\n", genetic->generation_number); + + for (i = 0, j = 1; i < genetic->debug_size; j++) { + n += sprintf(page+n, "%d: %-12ld:\t", j, genetic->debug_history[i++]); + + /* assume num_genes is the same for all children */ + for (k = 0; k < genetic->child_ranking[0]->num_genes; k++) { + n += sprintf(page+n, "%ld\t", genetic->debug_history[i++]); + } + n += sprintf(page+n, "\n"); + + if (j == genetic->num_children) { + n += sprintf(page+n, "\n"); + j = 0; + } + } + + return proc_calc_metrics(page, start, off, count, eof, n); +} +#endif /* GENETIC_DEBUG */ +#endif /* CONFIG_GENETIC_LIB */ extern struct seq_operations fragmentation_op; static int fragmentation_open(struct inode *inode, struct file *file) diff -puN /dev/null include/linux/genetic.h --- /dev/null Fri Mar 14 06:52:15 2003 +++ linux-2.6.10-moilanen/include/linux/genetic.h Mon Jan 10 17:09:54 2005 @@ -0,0 +1,118 @@ +#ifndef __LINUX_GENETIC_H +#define __LINUX_GENETIC_H +/* + * include/linux/genetic.h + * + * Jake Moilanen + * Copyright (C) 2004 IBM + * + * Genetic algorithm library + * + * 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. + */ + +#include +#include +#include + + +#define GENETIC_HISTORY_SIZE 0x10 +#define GENETIC_HISTORY_MASK (GENETIC_HISTORY_SIZE - 1) + +#define GENETIC_DEFAULT_NUM_MUTATIONS 8 + +/* XXX Make this an adjustable runtime variable */ +/* Percentage that an iteration can jump within the range */ +#define GENETIC_ITERATIVE_MUTATION_RANGE 20 + +/* the ratio is number mutations per total number genes */ +#define GENETIC_MAX_MUTATIONS_RATIO 20 +#define GENETIC_MIN_MUTATIONS_RATIO 1 + +#define GENETIC_DEBUG 1 +#define GENETIC_NUM_DEBUG_POINTS 4 + +#define gen_dbg(format, arg...) do { if (GENETIC_DEBUG) printk(KERN_EMERG __FILE__ ": " format "\n" , ## arg); } while (0) +#define gen_trc(format, arg...) do { if (GENETIC_DEBUG) printk(KERN_EMERG __FILE__ ":%s:%d\n" , __FUNCTION__, __LINE__); } while (0) + +struct gene_param_s; + +struct genetic_child_s { + struct list_head child; + long fitness; + unsigned long num_genes; + void *genes; + struct gene_param_s *gene_param; + void *stats_snapshot; +}; + +typedef struct genetic_child_s genetic_child_t; + +/* Here's a generic idea of what it the genes could look like */ +struct gene_param_s { + unsigned long min; + unsigned long max; + unsigned long initial; + void (*mutate_gene)(genetic_child_t *, unsigned long); +}; + +typedef struct gene_param_s gene_param_t; + +struct genetic_s { + struct list_head children_queue[2]; + struct list_head *run_queue; + struct list_head *finished_queue; + unsigned long child_life_time; + unsigned long num_children; /* Must be power of 2 */ + unsigned long natural_selection_cutoff; /* How many children + * will survive + */ + unsigned long num_mutations; + + void (*natural_selection)(struct genetic_s *); + + char *name; + struct timer_list timer; + struct genetic_ops *ops; + + genetic_child_t **child_ranking; + + /* performance metrics */ + long avg_fitness; + long last_gen_avg_fitness; + + unsigned long generation_number; + unsigned long child_number; + + unsigned long fitness_history_index; + long fitness_history[GENETIC_HISTORY_SIZE]; +#if GENETIC_DEBUG + unsigned long debug_size; /* number of longs in + debug history */ + unsigned long debug_index; + long *debug_history; +#endif +}; + +typedef struct genetic_s genetic_t; + +struct genetic_ops { + void (*create_child)(genetic_child_t *); + void (*set_child_genes)(void *); + void (*calc_fitness)(genetic_child_t *); + void (*combine_genes)(genetic_child_t *, genetic_child_t *, + genetic_child_t *); + void (*mutate_child)(genetic_child_t *); +}; + +extern int __init genetic_init(genetic_t * genetic, struct genetic_ops * ops, unsigned long num_children, unsigned long child_life_time, char * name, unsigned long max_num_genes); +extern void genetic_generic_mutate_child(genetic_child_t * child); +extern void genetic_generic_combine_genes(genetic_child_t * parent_a, + genetic_child_t * parent_b, + genetic_child_t * child); + + +#endif diff -puN lib/Kconfig~genetic-lib lib/Kconfig --- linux-2.6.10/lib/Kconfig~genetic-lib Mon Jan 10 17:09:54 2005 +++ linux-2.6.10-moilanen/lib/Kconfig Mon Jan 10 17:41:51 2005 @@ -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 # diff -puN lib/Makefile~genetic-lib lib/Makefile --- linux-2.6.10/lib/Makefile~genetic-lib Mon Jan 10 17:09:54 2005 +++ linux-2.6.10-moilanen/lib/Makefile Mon Jan 10 17:09:54 2005 @@ -23,6 +23,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/ diff -puN /dev/null lib/genetic.c --- /dev/null Fri Mar 14 06:52:15 2003 +++ linux-2.6.10-moilanen/lib/genetic.c Mon Jan 10 17:28:46 2005 @@ -0,0 +1,507 @@ +/* + * Genetic Algorithm Library + * + * Jake Moilanen + * Copyright (C) 2004 IBM + * + * + * 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. + */ + +/* + * Life cycle + * + * 1.) Create random children + * 2.) Run tests + * 3.) Calculate fitness + * 4.) Take top preformers + * 5.) Make children + * 6.) Mutate + * 7.) Goto step 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +char genetic_lib_version[] = "0.1"; + +int mutate_rate_change = 1; + +static void genetic_ns_top_parents(genetic_t * genetic); +static void genetic_ns_award_top_parents(genetic_t * genetic); +static void genetic_create_children(genetic_t * genetic); +static void genetic_split_performers(genetic_t * genetic); +static void genetic_mutate(genetic_t * genetic); +static void genetic_run_child(genetic_t * genetic); +static void genetic_new_generation(genetic_t * genetic); + +void genetic_switch_child(unsigned long data); +struct proc_dir_entry * genetic_root_dir = 0; +extern int genetic_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); +#if GENETIC_DEBUG +extern int genetic_debug_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); +#endif + + +int __init genetic_init(genetic_t * genetic, struct genetic_ops * ops, + unsigned long num_children, unsigned long child_life_time, + char * name, unsigned long max_num_genes) +{ +#define DEBUG_FILE "-debug" + struct proc_dir_entry *entry; + char *debug_name = (char *)kmalloc(strlen(name) + sizeof(DEBUG_FILE), GFP_KERNEL); + if (!debug_name) { + printk(KERN_ERR "genetic_init: not enough memory\n"); + return -ENOMEM; + } + + printk(KERN_INFO "Initializing Genetic Library - version %s\n", genetic_lib_version); + + genetic = (genetic_t *)kmalloc(sizeof(genetic_t), GFP_KERNEL); + if (!genetic) { + printk(KERN_ERR "genetic_init: not enough memory\n"); + kfree(debug_name); + return -ENOMEM; + } + + genetic->name = (char *)kmalloc(strlen(name), GFP_KERNEL); + if (!genetic->name) { + printk(KERN_ERR "genetic_init: not enough memory\n"); + kfree(debug_name); + kfree(genetic); + return -ENOMEM; + } + + genetic->child_ranking = (genetic_child_t **)kmalloc(num_children * sizeof(genetic_child_t *), GFP_KERNEL); + if (!genetic->child_ranking) { + printk(KERN_ERR "genetic_init: not enough memory\n"); + kfree(debug_name); + kfree(genetic->name); + kfree(genetic); + return -ENOMEM; + } + + /* Init some of our values */ + strcpy(genetic->name, name); + + INIT_LIST_HEAD(&genetic->children_queue[0]); + INIT_LIST_HEAD(&genetic->children_queue[1]); + + genetic->run_queue = &genetic->children_queue[0]; + genetic->finished_queue = &genetic->children_queue[1]; + + genetic->ops = ops; + genetic->num_children = num_children; + genetic->child_life_time = child_life_time; + + genetic->generation_number = 1; + genetic->child_number = 0; + genetic->num_mutations = GENETIC_DEFAULT_NUM_MUTATIONS; + genetic->natural_selection = genetic_ns_top_parents; + genetic->natural_selection_cutoff = num_children / 2; + genetic->avg_fitness = 0; + genetic->last_gen_avg_fitness = 0; + +#if GENETIC_DEBUG + genetic->debug_index = 0; + /* create array for history. The +1 on max_num_genes is for the + fitness */ + genetic->debug_size = num_children * (max_num_genes + 1) * GENETIC_NUM_DEBUG_POINTS; + + genetic->debug_history = (long *) kmalloc(genetic->debug_size * sizeof(long), GFP_KERNEL); + +#endif + + /* Create some children */ + genetic_create_children(genetic); + + /* Setup how long each child has to live */ + init_timer(&genetic->timer); + genetic->timer.function = genetic_switch_child; + genetic->timer.data = (unsigned long)genetic; + +#ifdef CONFIG_PROC_FS + + /* Setup proc structure to monitor */ + if (!genetic_root_dir) + genetic_root_dir = proc_mkdir("genetic", 0); + + entry = create_proc_entry(name, 0644, genetic_root_dir); + + if (entry) { + entry->nlink = 1; + entry->data = genetic; + entry->read_proc = genetic_read_proc; + } + +#if GENETIC_DEBUG + strcpy(debug_name, name); + strcat(debug_name, DEBUG_FILE); + entry = create_proc_entry(debug_name, 0644, genetic_root_dir); + + if (entry) { + entry->nlink = 1; + entry->data = genetic; + entry->read_proc = genetic_debug_read_proc; + } +#endif /* GENETIC_DEBUG */ +#endif /* CONFIG_PROC_FS */ + + genetic_run_child(genetic); + + printk(KERN_INFO "%ld children started in %s genetic library\n", num_children, name); + + kfree(debug_name); + return 0; +} + +/* create some children, it is up to the lib user to come up w/ a good + distro of genes for it's children */ +static void genetic_create_children(genetic_t * genetic) +{ + unsigned long i; + genetic_child_t * child; + + for (i = 0; i < genetic->num_children; i++) { + genetic->child_ranking[i] = (genetic_child_t *)kmalloc(sizeof(genetic_child_t), GFP_KERNEL); + child = genetic->child_ranking[i]; + + genetic->ops->create_child(child); + + list_add_tail(&child->child, genetic->run_queue->next); + } +} + +/* See how well child did and run the next one */ +void genetic_switch_child(unsigned long data) +{ + genetic_t * genetic = (genetic_t *)data; + genetic_child_t * child; + + child = list_entry(genetic->run_queue->next, genetic_child_t, child); + + list_del(&child->child); + + list_add_tail(&child->child, genetic->finished_queue->next); + + genetic->ops->calc_fitness(child); + + genetic->child_ranking[genetic->child_number++] = child; + + /* See if need more children */ + if (list_empty(genetic->run_queue->next)) + genetic_new_generation(genetic); + + genetic_run_child(genetic); +} + +/* Set the childs genes for run */ +void genetic_run_child(genetic_t * genetic) +{ + genetic_child_t * child = list_entry(genetic->run_queue->next, genetic_child_t, child); + void * genes = child->genes; + + BUG_ON(!genes); + + genetic->ops->set_child_genes(genes); + + /* set a timer interrupt */ + genetic->timer.expires = jiffies + genetic->child_life_time; + add_timer(&genetic->timer); + +} + +/* This natural selection routine will take the top + * natural_select_cutoff and use them to make children for the next + * generation and keep the top half perfomers + * + * This assumes natural_select_cutoff is exactly half of num_children + * and num_children is a multable of 4. + */ +static void genetic_ns_top_parents(genetic_t * genetic) +{ + unsigned long i,j,k = 0; + unsigned long num_children = genetic->num_children; + unsigned long cutoff = num_children - genetic->natural_selection_cutoff; + + for (i = cutoff, j = num_children - 1; i < j; i++, j--, k += 2) { + /* create child A */ + genetic->ops->combine_genes(genetic->child_ranking[i], + genetic->child_ranking[j], + genetic->child_ranking[k]); + + /* create child B */ + genetic->ops->combine_genes(genetic->child_ranking[i], + genetic->child_ranking[j], + genetic->child_ranking[k+1]); + } +} + +/* This natural selection routine just has top parents populating + bottom performers. */ +static void genetic_ns_award_top_parents(genetic_t * genetic) +{ + unsigned long i; + unsigned long num_children = genetic->num_children; + unsigned long cutoff = num_children - genetic->natural_selection_cutoff; + + for (i = 0; i < cutoff; i += 2) { + genetic->ops->combine_genes(genetic->child_ranking[num_children - 1], + genetic->child_ranking[num_children - 2], + genetic->child_ranking[i]); + + genetic->ops->combine_genes(genetic->child_ranking[num_children - 1], + genetic->child_ranking[num_children - 2], + genetic->child_ranking[i+1]); + } +} + + +static inline void genetic_swap(genetic_child_t ** a, genetic_child_t ** b) +{ + genetic_child_t * tmp = *a; + + *a = *b; + *b = tmp; +} + +/* bubble sort */ +/* XXX change this to quick sort */ +static void genetic_split_performers(genetic_t * genetic) +{ + int i, j; + + for (i = genetic->num_children; i > 1; i--) + for (j = 0; j < i - 1; j++) + if (genetic->child_ranking[j]->fitness > genetic->child_ranking[j+1]->fitness) + genetic_swap(&genetic->child_ranking[j], &genetic->child_ranking[j+1]); +} + +static void genetic_mutate(genetic_t * genetic) +{ + long child_entry = -1; + int i; + + for (i = 0; i < genetic->num_mutations; i++) { + get_random_bytes(&child_entry, sizeof(child_entry)); + child_entry = child_entry % genetic->num_children; + + genetic->ops->mutate_child(genetic->child_ranking[child_entry]); + } +} + +/* XXX This will either aid in handling new workloads, or send us on a + downward spiral */ +static void genetic_shift_mutate_rate(genetic_t * genetic, long prev_gen_avg_fitness, long avg_fitness) +{ + if (mutate_rate_change) { + /* XXX TODO: make this a ratio depending on total number + of genes */ + if (avg_fitness > prev_gen_avg_fitness) + genetic->num_mutations--; + else if (avg_fitness < prev_gen_avg_fitness) + genetic->num_mutations++; + + if (genetic->num_mutations > GENETIC_MAX_MUTATIONS_RATIO) + genetic->num_mutations = GENETIC_MAX_MUTATIONS_RATIO; + else if (genetic->num_mutations < GENETIC_MIN_MUTATIONS_RATIO) + genetic->num_mutations = GENETIC_MIN_MUTATIONS_RATIO; + } + +} + +static void genetic_calc_stats(genetic_t * genetic) +{ + long total_fitness = 0; + long prev_gen_avg_fitness = genetic->last_gen_avg_fitness; + int i; + + /* calculate the avg fitness for this generation and avg fitness + * so far */ + for (i = 0; i < genetic->num_children; i++) + total_fitness += genetic->child_ranking[i]->fitness; + + genetic->last_gen_avg_fitness = total_fitness / (long)genetic->num_children; + + genetic_shift_mutate_rate(genetic, prev_gen_avg_fitness, genetic->last_gen_avg_fitness); + + genetic->avg_fitness = ((genetic->avg_fitness * (long)(genetic->generation_number - 1)) + + genetic->last_gen_avg_fitness) / (long)genetic->generation_number; + + genetic->fitness_history[genetic->fitness_history_index++ & GENETIC_HISTORY_MASK] = + genetic->last_gen_avg_fitness; + +} + +#if GENETIC_DEBUG +/* Stores attributes into an array in the following format + * fitness gene[0] gene[1] .... gene[num_genes-1] + * Add +1 to GENETIC_NUM_DEBUG_POINTS if add another dump_children + * call + */ +void dump_children(genetic_t * genetic) +{ + int i, j; + long * genes; + unsigned long debug_size = genetic->debug_size; + + for (i = 0; i < genetic->num_children; i++) { + genetic->debug_history[genetic->debug_index++ % debug_size] = genetic->child_ranking[i]->fitness; + + genes = (long *)genetic->child_ranking[i]->genes; + + for (j = 0; j < genetic->child_ranking[i]->num_genes; j++) { + genetic->debug_history[genetic->debug_index++ % debug_size] = genes[j]; + } + } +} +#else +void dump_children(genetic_t * genetic) { return; } +#endif + +void genetic_new_generation(genetic_t * genetic) +{ + struct list_head * tmp; + + dump_children(genetic); /* dump_child #1 */ + + /* figure out top performers */ + genetic_split_performers(genetic); + + dump_children(genetic); /* dump_child #2 */ + + /* calc stats */ + genetic_calc_stats(genetic); + + /* make some new children */ + genetic->natural_selection(genetic); + + dump_children(genetic); /* dump_child #3 */ + + /* mutate a couple of the next generation */ + genetic_mutate(genetic); + + dump_children(genetic); /* dump_child #4 */ + + /* Move the new children still sitting in the finished queue to + the run queue */ + tmp = genetic->run_queue; + genetic->run_queue = genetic->finished_queue; + genetic->finished_queue = tmp; + + genetic->child_number = 0; + genetic->generation_number++; + +} + +/* Mutate a gene picking a random value within the gene range */ +void genetic_generic_random_mutate_gene(genetic_child_t * child, long gene_num) +{ + unsigned long *genes = (unsigned long *)child->genes; + unsigned long min = child->gene_param[gene_num].min; + unsigned long max = child->gene_param[gene_num].max; + unsigned long gene_value; + unsigned long range = max - min + 1; + + /* create a mutation value */ + get_random_bytes(&gene_value, sizeof(gene_value)); + + gene_value = gene_value % range; + + genes[gene_num] = min + gene_value; +} + +void genetic_generic_iterative_mutate_gene(genetic_child_t * child, long gene_num) +{ + unsigned long *genes = (unsigned long *)child->genes; + long min = child->gene_param[gene_num].min; + long max = child->gene_param[gene_num].max; + long change; + long old_value = genes[gene_num]; + long new_value; + unsigned long range = max - min + 1; + + /* If under 5, random might work better */ + if (range < 5) + return genetic_generic_random_mutate_gene(child, gene_num); + + /* get the % of change */ + get_random_bytes(&change, sizeof(change)); + + change = change % GENETIC_ITERATIVE_MUTATION_RANGE; + + + new_value = ((long)(change * range) / (long)100) + old_value; + + if (new_value > max) + new_value = max; + else if (new_value < min) + new_value = min; + + genes[gene_num] = new_value; +} + +/* This assumes that all genes are a unsigned long array of size + num_genes */ +void genetic_generic_mutate_child(genetic_child_t * child) +{ + long gene_num = -1; + + /* pick a random gene */ + get_random_bytes(&gene_num, sizeof(gene_num)); + + if (gene_num < 0) + gene_num = -gene_num; + + gene_num = gene_num % child->num_genes; + + if (child->gene_param[gene_num].mutate_gene) + child->gene_param[gene_num].mutate_gene(child, gene_num); + else + genetic_generic_random_mutate_gene(child, gene_num); +} + + +/* Randomly pick which parent to use for each gene to create a child */ +void genetic_generic_combine_genes(genetic_child_t * parent_a, + genetic_child_t * parent_b, + genetic_child_t * child) +{ + unsigned long * genes_a = (unsigned long *)parent_a->genes; + unsigned long * genes_b = (unsigned long *)parent_b->genes; + unsigned long * child_genes = (unsigned long *)child->genes; + + /* Assume parent_a and parent_b have same num_genes */ + unsigned long num_genes = parent_a->num_genes; + int parent_selector; + int i, j; + + + + for (i = 0; i < num_genes; i++) { + get_random_bytes(&parent_selector, sizeof(parent_selector)); + + /* Look at each bit to determine which parent to use */ + for (j = 0; j < (sizeof(parent_selector) * 8); j++) { + if (parent_selector & 1) { + child_genes[i] = genes_a[i]; + } else { + child_genes[i] = genes_b[i]; + } + parent_selector >>= 1; + } + } +} _