[Galaxy S I9000 Android Development] pikachu01:
Attention!

If you're looking for scripts, here it is:

[CWM][SCRIPTS][TWEAKS] Thunderbolt!

Script Reviews are here:

Script Reviews

Introduction

I've been meaning to share this with the whole Android community. It seems unfair that only the i9000 folks have access to the vast research that I've done so far. Hence I'm sharing this in the general Android thread in hopes that it'll benefit everyone in the long run :)

A lot of people often asked about how Android really works and the optimizations can be made to their Android to make it perform better in terms of:

- battery life

- raw performance

- GUI smoothness

With Android based on Linux, and with the experience I have with Linux/Unix (I deal with Unix/Linux on a daily basis for my work) I managed to find some tweaks that we can do to optimize our phones.

I also hope that people will experiment with the tweaks posted here and feedback to me if there are certain ways that I'm doing incorrectly or there are better ways than what I've posted here.

Script tweaks

There are a lot of scripts in XDA that specifically uses the optimizations (Linux or Android based) that I will explain below to actually increase performance/battery life. However, you need to know what those scripts do before using it.

I've done some reviews of the script here:

Script Reviews by Pikachu01

There are good scripts that are properly tested and made sure that it works. It's a script by Zacharias maladroit, the kernel dev who makes the platypus kernel for CM7 and snail kernel for i9000. I modded it to be compatible with i9000 and it'll probably do more than some of the scripts I reviewed ;)

From there, I've also taken the liberty of modifying it some more based on my knowledge and research I've done. It's called ThunderBolt!:

LINK

Now onwards to what actually goes on beneath the hood.

Android Governors

Some custom kernels provide customized governors for you to choose. These are currently known:

- Ondemand (CPU scales to the highest frequency immediately after a certain CPU threshold)

- Conservative (CPU scales to the highest or 1 scale lower than that after a certain threshold gradually and scales down if CPU is below a certain threshold

- OndemandX (Has sleep codes that scales down the CPU when device is asleep. Threshold is set to scale slower)

OndemandX

OndemandX has a suspend frequency that it maintains when the phone is sleeping. It does have a known issue of waking up too slowly when a call comes in.

Conservative

Conservative is used when you would like the CPU to scale to the maximum through each frequency slowly before reaching the maximum. It saves more battery at the expense of smoothness. There are a few tunables that can be configured in Conservative governor, namely the freq_step and both the up and down thresholds.

freq_step is a tunable that determines how much frequency (a percentage of the maximum frequency of your device) to increase at each sampling rate. e.g. you have 5 as freq_step and your maximum frequency is 1GHz. At each sampling rate, it'll increase by 50MHz. If your current frequency is 100MHz, it'll increase to 150MHz at the next sampling. What if your device doesn't support 150MHz? This is done by splitting time between 100MHz and 200MHz (assuming your next frequency is 200MHz) so that in average, your phone is performing at 150MHz.

The up_threshold is how much threshold to wait for before increasing the base frequency to the next freq_step. I.e. if it is 80, the CPU will wait until it is 80% loaded before increasing the frequency. The down threshold is the opposite of up_threshold, which is the threshold to wait until the CPU downscales the frequency to the next frequency step.

Ondemand

Ondemand is a governor that basically a simpler version of conservative. One difference is that it scales to the maximum frequency after load has exceeded a threshold. This is usually 80. After that, it looks at the load again when it's at the max frequency. If it's below 80 (example), it'll scale down one frequency step. The frequency step mentioned here is the available frequency step. I.e. if your device has frequencies of 100,200,400,800 to 1GHz then it'll scale down from 1GHZ to 800, 400 then 200 and 100.

One optimization I have for Ondemand for i9000 TalonDev is that I tuned it to be 80 rather than 65. I have not felt any slowdowns from using this setting and it saves a tiny bit more battery. You can experiment on this value however you want, but bear in mind that I'm not responsible for any slowdowns or damage that is caused by it. Read up a bit more on thresholds to know more (Google it).

I/O scheduler tweaks

Another tweak that can be done is the I/O scheduler tweak. Some kernels come with a few standard I/O schedulers while others have extra schedulers that you can choose from. I'll explain the few that I know and if you have more information on the schedulers, you can post it here for everyone to discuss/share :)

CFQ - Completely fair. Most will come with this, but this is not optimized for android. Some kernels do optimize it and use it.

Noop - Simple and least overhead of all I/O schedulers. Produces the best outcome for some cases and some do swear by it. It's downside is that it is easily bogged down by high I/O transactions.

Deadline - Optimized for mobile-like devices like Android. Also, some do swear by this. If its writes_starve sysfs is tweaked to be fair, it's the same as SIO.

SIO - It's a fair deadline scheduler. It's the best scheduler. Nuff said.

VR - A newer I/O scheduler that places penalty on reverse seeks. Not for flash drives, though the R penalty can be tweaked to be a multiplier of 1. Even if this is done, it has a high overhead.. Read more here: http://www.gelato.unsw.edu.au/IA64wiki/IOScheduling/VRscheduler

BFQ - Too high of an overhead, it is optimized for spindle disks.

You can pick and choose from Voltage Control or other UV/OC apps and apply it immediately to feel the difference.

Here's my assessment of all the schedulers that I know:

SIO> NOOP> Deadline > VR > BFQ > CFQ

NOOP is a simple I/O scheduler without overhead that tries to do each I/O transaction as it comes (FIFO). When a group of transactions is detected, it will try to merge it together to make batches of transactions (makes the whole transaction faster). NOOP doesn't have starvation detection, hence if an I/O transaction takes a painfully long amount of time, it will still continue to do it rather than switch the CPU into doing something else e.g. GUI interrupts (i.e. scrolling lists, flicking screens). All other schedulers also have the "merge" feature. NOOP is the only one that makes the "merge" feature its only feature.

CFQ is a complex I/O scheduler that tries to determine the address space of the transaction and applies a cost algorithm in that if the address is close together, it will group them up and perform them. It also tries to make the transaction incremental (i.e. reading/writing through address incrementally so that the disk spindle needs to wind down the least in conventional platter hard disks) The problem is, our flash devices have very little delta between reading a far reaching address space (than the one currently being written/read) or a closer one as it doesn't rely on spindle/rotations. Hence, having this costing algorithm adds overhead and slows down the overall transaction. CFQ has a lot of algorithms to make sure each process gets a fair slice of time on I/O transcations. Too much overhead.

Deadline has a starvation detector and is simple enough that it doesn't have all the overhead of doing rotational/costing disks algorithm. However, reads are done 2x more likely than writes as it has a algorithm based on weights in that reads must be done first if both a read and a write is detected. It has a 2:1 ratio of read to write weights coded into the scheduler (that can be tweaked - writes_starved, will include it in the next version of system_tweak). Hence it's not a fair scheduler.

SIO is Simple I/O that tries to implement a NOOP type scheduler that has starvation detection. Hence, long I/O transactions will be preempted and given CPU time only after other faster transactions are completed, guaranteeing smoothness. Also, it doesn't have overhead of calculating costs. Also, it has a fair number of writes to read, guaranteeing that all transactions are equal.

BFQ has a lot of algorithms to make sure each process gets a fair slice of bandwidth (Budget Fair Queueing). Too much overhead.

V(R) tries to make sure that each transaction has a weight associated with it, being R. And if the seek is reversed, the R will be multiplied by a penalty making it less likely to be processed. Not for flash drives as reverse seek in a flash drive is just as fast as a forward seek.

In benchmarking tests, the tests normally consists of testing the time it takes for a contiguous I/O transfer from 1 point to another. NOOP excels at that because it won't let itself get interrupted to perform another I/O task. This would mean that in real life testing, NOOP will let a long arduous task to finish while at the expense of UI functionality (you will get UI lags)

SIO on the other hand will perform badly at benchmarking as it gets pre-empted when the contiguous tasks takes more than 0.5secs (for synchronous tasks as benchmarking tools perform a synchronous I/O task from one point to another) to another more important task like UI interrupt (when you're scrolling) or Kernel interrupt (when your kernel needs to perform garbage collecting or swap memory etc)

The 0.5 secs can be tuned in the SIO tuneables though, but I would think 0.5 secs for synchronous tasks and 5 secs for async tasks is pretty good to maintain a balance between long/short tasks enabling a smoother experience in Android.

LowMemoryKiller

The LowMemoryKiller is a constant debate between more free RAM and more multitasking capabilities as free RAM (more than 60MB free) is actually wasted RAM. The fact is SGS only has a puny amount of RAM left after a few big chunks are allocated to the Graphics, Modem, Sound and Video (and some others that I do not know a lot about). With that knowledge, Samsung decided to give the SGS SL a bigger amount of RAM when they released it ( about ~650MB of RAM read that from somewhere, can't remember).

The LowMemoryKiller is actually a feature in the Android OS for better memory management.

You can read about it more here:

http://xdaforums.com/showpost.php?p=5442369&postcount=1

This is an important feature due to the perennial problem of having low free memory causing lagginess and slowness in launching apps. When you have free memory lingering around the number of 40MB or less, the Android OS just lags like hell.

What this would mean is, you would want to tweak the LMK to not have the situation of it having less than 40MB (or even close to that).

The modern Linux machine in the Android ecosystem relies on a mechanism called Low Memory Killer (LMK) to consistently free up RAM. This is due to Android's internal mechanism of caching apps (and never fully exiting them) when you press the back button. This is to enable faster app switching and provide a seamless experience for apps usage model. Android also, by itself will also constantly look for often used apps to cache them for faster app opening. This will happen even before your system fully boots.

Now, when you mention LMK, the most obvious thoughts that come up are minfrees and Out Of Memory (OOM) groupings. Yes, those two are integral parts when it comes to LMK. The issue here is that no one actually mentioned that there are two LMK systems in Android, that being:

- Linux LMK

- Android Dalvik VM LMK

Both are separate entities that kills/removes app/dalvik cache from RAM when RAM reaches a certain level.

What confuses most people (including me) is that the OOM groupings of both mechanisms have the same names being (Android 2.3 based):

LMK App Categories

FOREGROUND_APP: Your apps in the foreground, being used currently, interfacing with the user.

VISIBLE_APP: Visible app that is still viewable by the user, but not interacting with the user, example could be apps that reside in the status bar, and giving live information i.e. Os monitor graphs.

PERCEPTIBLE_APP: App that the user can still perceive i.e. Music app that is playing music.

HEAVY_WEIGHT_APP: RAM heavy apps that are not being interacted with, but will be a pain to load if its information is cleared in the memory.

SECONDARY_SERVER: App that acts as a secondary server. Not sure what this really means, but client-server thingy but secondary? Could be something that syncs with an app, but is not syncing currently?

BACKUP_APP: App that is currently making a backup (like Titanium backup?)

HOME_APP: Your launcher.

HIDDEN_APP: An exited app that still significant residual memory in the RAM. Exited only some time ago, or is constantly used by the user.

EMPTY_APP: An exited app that only has small remnants of residual memory in RAM. App that is exited quite some time ago.

Take note that this is my understanding of the OOM groupings. If there are mistakes, please correct me :)

Tuning LMK

Both the LMK and Dalvik VM has this groupings and they can be tunable by using prop and lowmemorykiller/parameters/adj respectively.

One errata about this is that the Dalvik LMK has one extra tunable which is CONTENT_PROVIDER, which affects widgets that are not currently refreshing or displaying information. The ADJ (which I will explain later) is not available to be tuned though. And it is clearly missing from the Linux LMK.

The ADJ is the so-called priority of the app categories, with 0 being the highest (should not be killed) and 15 being the lowest (should be the first to be killed). I haven't seen values other than 0-15 but I have a feeling that the numbers are arbitrary, and can be extended up to 65535, but who would want to do that since they're only 9 app categories :P

LMK are specified by pages, with memory = 4 * pages.

e.g.

4096 pages represents 16MB RAM (16 *1.024)

From the Linux LMK source code, the parameters can be adjusted as such:

* For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and
* "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes
* with a oom_adj value of 8 or higher when the free memory drops below 4096 pages
* and kill processes with a oom_adj value of 0 or higher when the free memory
* drops below 1024 pages.

Take note that the oom_adj so far takes only 6 values (although I am confident that it can take more than that). Haven't experimented enough to validate that, but I shouldn't need to as assigning different oom_adj should be a given for all the app categories. This is to make the LMK intelligent enough to determine which app to kill first rather than grouping different app categories into one priority, in which a lot of popular LMK settings that devs provide do that.

And for the Dalvik LMK, my understanding is that it will remove all dalvik caches of <INSERT_APP_CATEGORY_ADJ> or higher if free RAM/pages reaches the level specified.

Hence, two things are learned here:

- Linux LMK will kill an app category of that particular RAM level or higher if free RAM drops below a certain level e.g.

Hidden_APP has a minfree of 16384 pages. If pages fall below this level, Empty_App will be killed first, free pages is recalculated, then if RAM is still below that, Hidden_Apps will be killed next.

- Dalvik LMK will clear all dalvik caches in RAM of all the App category if free RAM reaches the level e.g.

SECONDARY_SERVER_MEM is 8192 pages. When RAM reaches 8192 pages, all SECONDARY_SERVER caches are cleared.

Best Practices

Now, how do we tweak both so that it is efficient in doing its job, i.e. LMK.

From my experiments I can safely say that:

- Below 60MB free RAM you will feel lag 5-10% of the time.

- Below 52MB free RAM you will feel lag 5-15% of the time.

- Below 46MB free RAM you will feel lag 15-20% of the time.

- Below 42MB free RAM you will feel lag 20-50% of the time.

- Below 36MB free RAM you will feel lag 100% of the time.

The experience of lag increases exponentially from 60 down to 36MB. Hence, the LMK should be tweaked against that.

Also, it is considered best practices to group oom_adj settings of Linux LMK with the Dalvik LMK as when free RAM drops to that level, both the Linux LMK and Dalvik LMK will work together to free up at both sides.

The 6 oom_adj that can be tuned for Linux minfree also need to be tuned correctly, failing this would make the LMK too aggressive/under aggressive in mid levels (even if your EMPTY_APP is aggressive, if your oom_adj is selected incorrectly, you'll get to a point where memory leak occurs).

Talon LMK - This is obsolete, TalonDev is using ThunderBolt! LMK from 0.5.x

Take for example, the Talon default LMK setting where:

LMK_ADJ is 0,1,2,3,7,15.

It's EMPTY_APP is 15

It's HIDDEN_APP is 5

It's HEAVYWEIGHT, SECONDARY and BACKUP is 3

Last three values of MINFREE is 4096, 9216, 12288.

Where's the issue here? If you noticed it congrats. :)

If you didn't, here's the issue:

The minfree 12288 is tied to EMPTY_APP which has a OOM_ADJ of 15.

The minfree 9216 is tied to nothing but has a OOM_ADJ of 7

The minfree 4096 is tied to HEAVYWEIGHT,SECONDARY and BACKUP with the OOM_ADJ of 3.

The LMK_ADJ is important as it represents the checkpoints where the Linux kernel will check for minfrees and kill them. Here's a situation:

Memory is below 12288 pages, OOM is triggered and Linux looks for EMPTY_APP to kill and kills all of them. Job done. Memory is back to >12288 pages.

After a few days, memory is below 12288 pages again, OOM kills all EMPTY_APP, but free memory is still lower than that. It sees if the next checkpoint is fulfilled, which is 9216 pages, and yes memory is still below 9216 pages. It looks for an OOM_ADJ of 7 and above to kill, but fails to find any as 7 and above OOM_ADJ only consists of EMPTY_APP.

Here comes the quandary. The next checkpoint is 4096. We're not ever going to reach that as you'll need memory to fall below 16MB, and at that time it'll be super sluggish to even support GUI. Hence BAM, memory leak or lag or something.

Supercharger LMK 512HP - Multitasking

Let me just paste this to avoid a lot of retyping :P

FOREGROUND_APP_ADJ=0
VISIBLE_APP_ADJ=4
PERCEPTIBLE_APP_ADJ=2
HEAVY_WEIGHT_APP_ADJ=5
SECONDARY_SERVER_ADJ=6
BACKUP_APP_ADJ=7
HOME_APP_ADJ=1
HIDDEN_APP_MIN_ADJ=8
EMPTY_APP_ADJ=15
FOREGROUND_APP_MEM=1536
VISIBLE_APP_MEM=3072
PERCEPTIBLE_APP_MEM=1024
HEAVY_WEIGHT_APP_MEM=10240
SECONDARY_SERVER_MEM=10240
BACKUP_APP_MEM=15360
HOME_APP_MEM=1024
HIDDEN_APP_MEM=15360
EMPTY_APP_MEM=25600
LMK_ADJ="0,4,6,8,14,15"
LMK_MINFREE="1536,3072,10240,15360,20480,25600"

A few things here:

It's EMPTY_APP and HIDDEN_APP is too large, 80 and 100MB respectively, but the 80MB is a false flag hence it doesn't really matter if you tweak that and you can get a very minor degree of multitasking because of this false flag. I'll get to that in a moment.

It's PERCEPTIBLE_APP has higher OOM_ADJ than VISIBLE_APP (odd selection, but RAM rarely goes below 40MB before it starts lagging like hell) People would reboot their phones when it gets to that, but it rarely gets to that for this configuration as LMK is pretty aggressive here.

Now, it's LMK_ADJ is "0,4,6,8,14,15"

15 is EMPTY_APP,

14 is nothing

8 is HIDDEN_APP - it looks like it'll only kill HIDDEN_APPs when it reaches 60MB, with HIDDEN_APPs being the heavy hitter of apps as most apps will end up in this category. Hence why multitasking is still doable (to an extent) but an aggressive LMK means taking a call or switching to a browser will kill your game (losing saves). Inconvenient, but it works for some people who haven't played a long game without saving and never took a call in between. Imagine the horror LOL!

Juwe's RAM Script

if [ -e /sys/module/lowmemorykiller/parameters/adj ]; then
echo "0,1,2,4,6,15" > /sys/module/lowmemorykiller/parameters/adj
fi

if [ -e /sys/module/lowmemorykiller/parameters/minfree ]; then
echo "2560,4096,5632,10240,11776,14848" > /sys/module/lowmemorykiller/parameters/minfree
fi

No OOM_ADJ are given, hence the stock ones are in effect here.

From init.rc, this is the stock 2.3.5 OOM_ADJ.

FOREGROUND 0

VISIBLE, PERCEPTIBLE 1

HEAVY_WEIGHT, SECONDARY_SERVER, BACKUP 2

HOME 4

HIDDEN_APP 7

EMPTY_APP 15

This one also suffers from the false flag of the 2nd last minfree not being used. It will start killing HIDDEN_APP at 40MB, which is usable (you'll only get lags 15-20% of the time, and most probably after long usage). Other than that, it's pretty stable.

Misc

There are a few known scripts that tweaks the LMK as well as the app priority (yes, the priority of the process categories can be changed too by using setprop.)

Supercharger is one of them:

http://xdaforums.com/showthread.php?t=991276

Take note that Talon doesn't permit Supercharger to be used (it will override it when booting through boot scripts) as Talon itself uses its own LMK settings that are optimized for ZRAM/Swap.

Kickasskernel script is also included in Supercharger. The script will clash with Zach's script in ThunderBolt! too as both of them tries to set different values for the same settings.

Note that Zach's script doesn't tweak LMK settings as i9000 Talon doesn't permit it. With that, if you're using Zach's scripts, you would need to find other ways of tweaking the LMK. Using Auto Memory Manager is good. You can download Supercharger, look at the presets and apply it using Auto Memory Manager. The preset is the only thing important here :)

LMK App Category Priorities

Using setprop, there is a way to set the priorities of the category. Since, app categories are different, I can only offer one recommendation: The HOME_APP_ADJ.

This prop setting sets how high of a priority your launcher is. A 1 or 2 is sufficient to make sure your launcher doesn't get killed. However, do you really want it to be on 1 or 2. Based on the previous article (LMK/OOM), you would know that OOM priorities are comparatively based on one another. Setting it 1 or 2 in stock 2.3.5 values would basically override PERCEPTIBLE/VISIBLE app priorities. Do you want your launcher to be killed only after your keyboard/music player to be killed? You decide :)

Journaling/Barriers

This has been a touchy subject here in XDA for most people who debate about it. Most recently, the SAS/Acid tweaks included a way to disable journaling on these partitions:

/system (System is read only, it's safe to remove journaling. However, you will not see speed increase by removing it as you're not writing onto /system 99.99% of the time unless you're using Titanium backup to remove system apps or copying init.d scripts to it)

/cache (Cache can be rebuilt on the fly. Data corruption on it is not game breaking)

/data (All of your data on your phone is here. Removing journaling can risk data corruption. Read more below)

On whether we need journaling or not, I will pose this situation:

Journaling is required to maintain data consistency in events that could lead to data corruption. Data could get corrupted in a number of situations:

- Misbehaving app that constantly writes without syncing/committing data to the disk

- Power loss due to forced reboots or bootloops when data is partially written/committed into disk

Android will normally buffer its writes before committing to the disk. This way, data stays in the RAM before data would only get written into the disk after a period of time. Take note that we as Android enthusiasts seek ways to better our phone to get the most out of it through experimental and sometimes wacky ways. Take OC/UV for example. When we OCed or UVed too much, sometimes our phone will get deadsleep or constant reboots. This leads to corruption if journaling is not turned on. However, we can get corruption too even with journaling but the risk is smaller.

Mount options too, play a role in whether journaling can be safe/unsafe. Most lagfixes will place a "barrier=1" in the /data to make sure that journals are actually committed before an out-of-order write is done. With that, the risk is greatly reduced at the cost of performance. You could experiment by removing barriers and see if it works for you. No guarantees on data if you remove barriers! More info on ext4/barriers:

http://lwn.net/Articles/283161/

http://kernelnewbies.org/Ext4#head-25c0a1275a571f7332fa196d4437c38e79f39f63

If you do disable journaling, make sure to manually run an fsck on your disk. Make sure the fsck binaries are in your binary paths (echo $PATH to show your binary paths). Don't ask how to do this if you're unable to do fsck. It is your own risk that you are disabling journaling/barriers.

One experiencing I would like to relate is this:

I've personally disabled ext4 journaling on my device. After experiencing a sudden power loss (forced reboot), my apps started FCing left and right and device became unstable that it sometimes rebooted when the device is sleeping. In other words, it's a lost case. Reflashing was the only way to restore my phone. This happened 1 day after disabling my journals. :(

Also note that ext2 is basically an ext4 without journaling. However, ext4 is updated all the time while ext2 source is not updated for quite some time (2 or 3 years?). Most optimizations are already in ext4 while ext2 even without journaling is slower than ext4 without journaling. A comparison of performance:

No journal:

ext4 > ext2

With Journal:

ext2 (can't do journaling in ext2) > ext4 > ext3

Update: Attached is a JournalingOn.zip for i9000 only that you can flash to rejournal your partitions (Only for i9000 devices!). Also attached is Acid Tweaksv6 - Removed Useless Stuff that will disable journaling only for /system and /cache, if anyone is interested. You will need to rejournal first before unjournaling. To be sure that /system and /cache has no journaling and your mounts are applied correctly, type "mount" without the quotes.

You'll be able to see that /data will have:

barrier=1, data=ordered

/system and /cache will have:

barrier=0, data=writeback

To be really sure, you can check by using tune2fs included in both the JournalingOn.zip and Acid Tweaksv6 - Removed Useless Stuff.zip.

Copy the tune2fs to /tmp and set rwxr-xr-x as its permissions using Root Explorer/File Expert

/tmp/tune2fs -l /dev/block/mmcblk0p2 | grep features

You can substitute the /dev/block/mmcblk0p2 (which is /data) to /dev/block/stl10 (/dbdata) /dev/block/stl9 (/system) /dev/block/stl11 (/cache) to check each partition.

Continued in Post #2 - Lack of space

Still posting ...