The current situation where apmd monitors system load and adjusts the hw.setperf sysctl has a number of flaws. While it's nice to keep the policy outside the kernel and allow tuning, the polling interval is too slow to be responsive. More aggressive polling intervals and we end up affecting the result, not just measuring it.
Here's a first cut at throwing some code into the kernel. You'll definitely want to kill apmd if you're running it. It's rather rough, in the wrong spot, and so on, but it's a place to start for further hacking. I'm not convinced we want to just copy the apmd policy into the kernel, so I haven't done so. Index: init_main.c =================================================================== RCS file: /cvs/src/sys/kern/init_main.c,v retrieving revision 1.168 diff -u -r1.168 init_main.c --- init_main.c 29 Jun 2010 20:25:57 -0000 1.168 +++ init_main.c 30 Jun 2010 03:51:06 -0000 @@ -172,6 +172,55 @@ EMUL_ENABLED | EMUL_NATIVE, }; +struct timeout setperf_to; + +void auto_setperf(void *v); +void +auto_setperf(void *v) +{ + int i; + long work, workdiff; + static long oldwork; + long idle, idlediff; + static long oldidle; + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + extern int perflevel; + + if (!cpu_setperf) + return; + + work = idle = 0; + CPU_INFO_FOREACH(cii, ci) { + for (i = 0; i < CPUSTATES; i++) { + if (i == CP_IDLE) + idle += ci->ci_schedstate.spc_cp_time[i]; + work += ci->ci_schedstate.spc_cp_time[i]; + } + } + workdiff = work - oldwork; + idlediff = idle - oldidle; + oldwork = work; + oldidle = idle; + + if (idlediff < workdiff / 5 ) { + perflevel += 10; + if (perflevel > 100) + perflevel = 100; + else + printf("going up %d\n", perflevel); + cpu_setperf(perflevel); + } else if (idlediff > workdiff * 7 / 10) { + perflevel -= 5; + if (perflevel < 0) + perflevel = 0; + else + printf("going down %d\n", perflevel); + cpu_setperf(perflevel); + } + + timeout_add_msec(&setperf_to, 100); +} /* * System startup; initialize the world, create process 0, mount root @@ -545,6 +594,9 @@ */ start_init_exec = 1; wakeup((void *)&start_init_exec); + + timeout_set(&setperf_to, auto_setperf, NULL); + timeout_add_msec(&setperf_to, 100); /* * proc0: nothing to do, back to sleep