Vor einiger Zeit habe ich ein kleines Shell-Script (SSSwitch – auto-switch settings) geschrieben, welches mein Android Handy beim Start optimiert und die Kernel-Einstellungen anpasst, wenn der Bildschirm an bzw. aus ist. Einige der Einstellungen kann man auch unter Linux verwenden. Als Beispiel beschreibe ich hier kurz, wie man die I/O Leistung verbessern kann und wer allgemein etwas zum optimieren von Linux / Ubuntu lesen möchte, dem hilft ggf. folgender Blog-Post weiter -> SpeedUp-Ubuntu ;)
Ein Ausschnitt aus dem besagtem Skript …
# =========
# One-time tweaks to apply on every boot;
# =========
STL=`ls -d /sys/block/stl*`;
BML=`ls -d /sys/block/bml*`;
MMC=`ls -d /sys/block/mmc*`;
… hier wurde der externe / interne Speicher von Android angegeben unter meinem Linux-System würde ich hier also z.B. folgendes angeben.
SDA=`ls -d /sys/block/sda/*`;
# =========
# Remount all partitions
# =========
for k in $(busybox mount | cut -d " " -f3);
do
sync;
busybox mount -o remount,noatime,nodiratime $k;
done;
“atime – Update inode access time for each access. See also the strictatime mount option.” – man mount Hier schalten wir die Funktion aus, welche aufzeichnet wann ein Datei oder Verzeichnis zuletzt angesehen wurde, da wir diese Funktionalität unter Android selten benötigen werden. ;) Ggf. kann man diesen Abschnitt komplett so unter Linux / Ubuntu verwenden z.B.:
mount
/dev/sda5 on / type ext4 (rw,errors=remount-ro,commit=0)
[...]
/dev/sda3 on /boot type ext2 (rw)
/dev/sda6 on /home type ext4 (rw,commit=0)
[...]
for k in $(busybox mount | cut -d " " -f3); do sync; mount -o remount,noatime,nodiratime $k; done;
mount
/dev/sda5 on / type ext4 (rw,noatime,nodiratime,errors=remount-ro)
[...]
/dev/sda3 on /boot type ext2 (rw,noatime,nodiratime)
/dev/sda6 on /home type ext4 (rw,noatime,nodiratime)
[...]
… wie man sieht sind die neuen Mount-Optionen nun aktiv. Wer mehr dazu erfahren möchte findet in dem bereits erwähnten “SpeedUp Ubuntu“-Beitrag unter dem Punkt “3.1) Filesystem” mehr Infos.
# =========
# check enabled/disabled status for IO settings
# =========
if [ "$IO_SETTINGS_ENABLED" -eq "1" ];
then
# =========
# Optimize non-rotating storage
# =========
for i in $STL $BML $MMC;
do
/system/xbin/echo "1" > $i/queue/iosched/rq_affinity;
/system/xbin/echo "1" > $i/queue/iosched/low_latency;
/system/xbin/echo "64" > $i/queue/max_sectors_kb;
/system/xbin/echo "$READ_AHEAD_ALL" > $i/queue/read_ahead_kb;
done;
Hier legen wir unter anderem die Puffergröße von unseren Speichermedien ein. Als Beispiel zeige ich kurz wie sich der Buffer auf die Lesegeschwindigkeit auswirkt, wenn eine Datei z.B. erneut geöffnet wird. ;) Dazu benötigen wir zuerst einen Compiler (gcc)
sudo apt-get install gcc
Nun den folgenden Quelltext kopieren …
#include
#include <stdio.h>
#include <unistd.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define MAX_PUFFER_GROESSE 1<<14
// Ausgabe
static void zeit_ausgabe(long int puff_groesse, clock_t realzeit, struct tms *start_zeit, struct tms *ende_zeit, long int schleiflaeufe);
int main(void) {
char puffer[MAX_PUFFER_GROESSE];
ssize_t n;
long int i, j=0, puffer_groesse, opt_puffer;
struct tms start_zeit, ende_zeit;
static long ticks=0;
clock_t uhr_start, uhr_ende, system_cpu=0.0;
// Ausgabe
fprintf(stderr, "+--------------+-------------+--------------+--------------+--------------+\n");
fprintf(stderr, "| %-10s | %-10s | %-10s | %-10s | %-10s |\n",
"Puffer-", "UserCPU", "SystemCPU", "Gebrauchte", "Schleifen-");
fprintf(stderr, "| %10s | %10s | %10s | %10s | %10s |\n",
" groesse", " (Sek)", " (Sek)", " Uhrzeit", " laeufe");
fprintf(stderr, "+--------------+-------------+--------------+--------------+--------------+\n");
while (j <= 14) {
i=0;
puffer_groesse=1<<j;
if (lseek(STDIN_FILENO, 0L, SEEK_SET) == -1) {
fprintf(stderr, "Error: lseek");
exit(1);
}
if (lseek(STDOUT_FILENO, 0L, SEEK_SET) == -1) {
fprintf(stderr, "Errir: lseek");
exit(1);
}
if ( (uhr_start = times(&start_zeit)) == -1) {
fprintf(stderr, "Error: times");
exit(2);
}
while ( (n=read(STDIN_FILENO, puffer, puffer_groesse)) > 0) {
if (write(STDOUT_FILENO, puffer, n) != n) {
fprintf(stderr, "Error: write");
exit(3);
}
i++;
}
if (n < 0) {
fprintf(stderr, "Error: read");
exit(4);
}
if ( (uhr_ende = times(&ende_zeit)) == -1) {
fprintf(stderr, "Error: times");
exit(5);
}
if (ticks == 0) {
if ( (ticks = sysconf(_SC_CLK_TCK)) < 0) {
fprintf(stderr, "Error: sysconf");
exit(6);
}
}
// Ausgabe
zeit_ausgabe(puffer_groesse, uhr_ende-uhr_start, &start_zeit, &ende_zeit, i);
j++;
}
exit(0);
}
// Ausgabe
static void zeit_ausgabe(long int puff_groesse, clock_t realzeit, struct tms *start_zeit, struct tms *ende_zeit, long int schleiflaeufe) {
static long ticks=0;
if (ticks == 0) {
if ( (ticks = sysconf(_SC_CLK_TCK)) < 0) {
fprintf(stderr, "Error: sysconf");
exit(6);
}
}
fprintf(stderr, "| %10ld | %10.2f | %10.2f | %10.2f | %10ld |\n", puff_groesse, (ende_zeit->tms_utime - start_zeit->tms_utime) / (double)ticks, (ende_zeit->tms_stime - start_zeit->tms_stime) / (double)ticks, realzeit / (double)ticks, schleiflaeufe);
return;
}
… und in eine leere Datei (io_speed_buffer.c) einfügen, nun compilieren wir noch schnell das Programm mit folgendem Befehl …
gcc io_speed_buffer.c -o io_speed_buffer
… als nächsten benötigen wir eine etwa 10 MB große Datei als Eingabe, diese erzeugen wir mittels “dd” im aktuellen Verzeichnis.
dd if=/dev/zero of=./test count=20000
Und schon können wir mittels folgenden Befehl die optimale Buffer-Größe herausfinden. Wobei man bei minimaler Verbesserung nicht den höheren Wert nutzen sollte.
z.B.:
./io_speed_buffer <test >test2
+--------------+-------------+--------------+--------------+--------------+
| Puffer- | UserCPU | SystemCPU | Gebrauchte | Schleifen- |
| groesse | (Sek) | (Sek) | Uhrzeit | laeufe |
+--------------+-------------+--------------+--------------+--------------+
| 1 | 0.48 | 10.41 | 10.90 | 10240000 |
| 2 | 0.26 | 3.40 | 3.66 | 5120000 |
| 4 | 0.19 | 1.64 | 1.83 | 2560000 |
| 8 | 0.12 | 0.80 | 0.92 | 1280000 |
| 16 | 0.01 | 0.46 | 0.47 | 640000 |
| 32 | 0.03 | 0.20 | 0.23 | 320000 |
| 64 | 0.00 | 0.12 | 0.12 | 160000 |
| 128 | 0.00 | 0.07 | 0.07 | 80000 |
| 256 | 0.00 | 0.04 | 0.04 | 40000 |
| 512 | 0.00 | 0.02 | 0.02 | 20000 |
| 1024 | 0.00 | 0.02 | 0.02 | 10000 |
| 2048 | 0.00 | 0.01 | 0.01 | 5000 |
| 4096 | 0.00 | 0.00 | 0.00 | 2500 |
| 8192 | 0.00 | 0.01 | 0.01 | 1250 |
| 16384 | 0.00 | 0.01 | 0.01 | 625 |
Standardmäßig ist der Wert “128” gesetzt …
cat /sys/block/sda/queue/read_ahead_kb
128
… welchen wir jedoch leicht ändern können! ;)
sudo echo "256" > /sys/block/sda/queue/read_ahead_kb
cat /sys/block/sda/queue/read_ahead_kb
256
Ggf. kann man diese Einstellungen auch unter Linux direkt beim start ausführen lassen z.B. könnte man den zuvor gezeigten “echo”-Befehl in der “rc.local”-Datei einfügen. (/etc/rc.local)
# =========
# Optimize io scheduler
# =========
for i in $STL $BML $MMC;
do
/system/xbin/echo "$IO_SCHEDULER" > $i/queue/scheduler;
Welcher I/O Scheduler der beste für das Speichermedium ist kann man am betesten selber testen, indem man z.B. wieder den “dd”-Befehl verwendet. -> [Discussion] SSSwitch – auto-switch settings PS: Für mein Android-System nutze ich momentan “bfq” wobei “noop” gerade für SSD-Speichermedien schneller sein soll …
case $IO_SCHEDULER in
"cfq")
/system/xbin/echo "0" > $i/queue/rotational;
/system/xbin/echo "1" > $i/queue/iosched/back_seek_penalty;
/system/xbin/echo "1" > $i/queue/iosched/low_latency;
/system/xbin/echo "3" > $i/queue/iosched/slice_idle;
/system/xbin/echo "16" > $i/queue/iosched/quantum;
/system/xbin/echo "2048" > $i/queue/nr_requests;
/system/xbin/echo "1000000000" > $i/queue/iosched/back_seek_max;;
"bfq")
/system/xbin/echo "0" > $i/queue/rotational;
/system/xbin/echo "1" > $i/queue/iosched/back_seek_penalty;
/system/xbin/echo "1" > $i/queue/iosched/low_latency;
/system/xbin/echo "3" > $i/queue/iosched/slice_idle;
/system/xbin/echo "16" > $i/queue/iosched/quantum;
/system/xbin/echo "2048" > $i/queue/nr_requests;
/system/xbin/echo "1000000000" > $i/queue/iosched/back_seek_max;;
"noop")
/system/xbin/echo "4" > $i/queue/iosched/quantum;
/system/xbin/echo "16" > $i/queue/iosched/fifo_batch;
/system/xbin/echo "248" > $i/queue/nr_requests;;
"deadline")
/system/xbin/echo "1" > $i/queue/iosched/front_merges;
/system/xbin/echo "16" > $i/queue/iosched/fifo_batch;;
"sio")
/system/xbin/echo "1" > $i/queue/iosched/front_merges;
/system/xbin/echo "4" > $i/queue/iosched/quantum;
/system/xbin/echo "16" > $i/queue/iosched/fifo_batch;
/system/xbin/echo "256" > $i/queue/nr_requests;;
esac;
[...]
done;
[...]
fi;
Hier noch ein-wenig Feintuning für die entsprechenden I/O Scheduler ;) ggf. müssen diese Werte auf anderen Systemen (Hardware) angepasst werden, dies habe ich bisher nur auf meinem Android System getestet. Falls euch / jemanden dieser Blog-Beitrag gefallen hat, werde ich weitere Einstellungen von dem Skript erklären … :)