While I am somewhat familiar with the process Binders use to communicate across processes, I was wondering if the fact that they have to interact with the Binder Driver on kernel significantly increased the memory overhead of creating the object.
Furthermore, does creating a significant number of Binders limit the transaction speed of existing Binders even if the number of transactions doesn't increase?
You'd have to dig into the libbinder code as well as the kernel driver code to really do an analysis of memory consumption. However, the overhead is likely not very much as internally the binder driver has its driver object then links to the actual binders owned by the calling process. It also limits the number of threads which can be used by calling processes to handle simultaneous transactions.
As far as performance goes, the main limiting factor is going to be the size of the transaction data through a given binder. Each binder gets a fixed size buffer (1MB) to handle all transactions for that binder. So if there are multiple transactions going on simultaneously for a specific binder, the total data used by all of them is what counts against this limit. This can be very tricky to troubleshoot or handle gracefully as the exception which occurs (Java level) does not indicate if it was the send part of the transaction or the receive part. The rule of thumb is that data moving across a binder needs to be small, like message passing. It's not well suited for something like streaming data. Android uses the ashmem driver support to better handle this as well as the ability to share file descriptors across binders.
The binders themselves are tracked in a btree, so lookup should be extremely fast. It would be interesting to have some stats on this for a given process as well as if the total number of binders in the system gets to a high level, but I'm not aware of any such data.
Related
I am sending data via broadcasts like this:
Intent outIntent = new Intent(Const.ACTION_FEED);
outIntent.putExtra(Const.EXTRA_FEED, data);
sendBroadcast(outIntent);
The issue is that data can get quite large, resulting in a TransactionTooLargeException. The documentation says:
The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process. Consequently this exception can be thrown when there are many transactions in progress even when most of the individual transactions are of moderate size.
Bottom line: it seems to be impossible to tell in advance what size is acceptable for data.
Furthermore:
The key to avoiding TransactionTooLargeException is to keep all transactions relatively small. [...] If possible, try to break up big requests into smaller pieces.
The nature of the data I am sending is such that I could easily break it down into smaller pieces and send them individually, once I have established that the whole thing is too big to send at once.
The logical step would be to wrap the whole code in a try/catch block, and upon receiving a TransactionTooLarge exception, chop up the data into smaller chunks and retry.
Alas, according to the logcat, the exception is not thrown at the caller’s end but in a system process. The system then goes on to crash the receiver of the broadcast, at which point any recovery is out of the sender’s control.
How can I tell how much data is OK to send as a broadcast extra, and prevent crashing the receiver of the data?
I am wondering if there'll be an extra cost to query the content provider within the same process. I'm aware that the Content Provider is transacting data by the binder, and all the data transition between binder service and binder client will be passing by binder driver in kernel space.
I suspect if it still uses the same approach while we use the content provider in the same process and therefore causes extra overhead such as latency, extra computing...etc.
Here's the scenario - in the multi-process app, you will normally require your storage system to use the binder system to pass data between processes, but at the same time, you will also need to pass data within the same process.
What's the common approach if you have a multiprocess android app? For now, I know a third-party solution MMKV but I am not sure if there's an official solution to avoid this kind of overhead (if it even exists).
I am wondering if there'll be extra cost to query content provider within the same process
There definitely will be overhead incurred by using ContentProvider, compared with simply calling methods on an object. Inter-process communication (IPC) is not free. This is one of the reasons for the complaints about performance of the Storage Access Framework, which relies heavily on ContentProvider.
The fact that the ContentProvider is in the same process as the consumer should not eliminate this overhead.
Doing some performance optimizations and considering running some tasks in parallel.
Specifically thinking some DB & network operations as they should not bottleneck each other.
The only thing I'm worried is if this will cause problems on lower end devices, still lots of single core phones around. The app has minSDK=10.
For each question please consider it in context of device compatibility:
Can independent Db & Network calls be ran in parallel without problems ?
Does it make sense to consider parallel read-only DB calls ?
Does it make sense to run Network tasks in parallel ?
Any conventions around how many threads should run in parallel ?
Thanks.
DB calls:
- usually databases do not give away 100% of the resources to one call, so yes, multiple calls might help, especially if one request takes the longer time to process in database, but the data are small, anything longer than 100ms,
in the case where the amount of data is big and processing the request in DB is short, then access in multiple calls and limited network speed could lead into issues, like timing etc,
you have to take into consideration if on given device the bottleneck would not be network speed or the customer with limited network etc.
network calls:
I would suggest that the criteria would be very same as with DB calls, lets say it can not be a shocker as DB calls are network calls:).
parallel read only calls to db:
yes, I know cases where it can be fruitful, but I would say for most cases on small devices, it would be too much hassle that could work only in few specific situations, so in general, I would not recommend that,
number of threads, that is device and app
specific question based on:
saving power is priority,
data throughput or latency is priority,
fast computed results from data is priority,
etc.
I have written an application in C which I am porting to Android using the NDK. This application makes use of fork() to create child processes periodically to perform some tasks. These child processes communicate with the main process through pipes and then exit after their task is complete. The amount of data transferred through pipes can be hundreds of kilobytes.
I'm attempting to keep the parent process very lean in terms of memory and CPU consumption and also very stable, but the child processes can sometimes take up a significant amount of resources. It doesn't matter much if the child processes are sometimes killed by the system due to this. The parent process can gracefully handle these conditions.
For this use case, is it fine to keep the fork()s in the native code? Is there a better way of doing this through Android's framework?
Thank you.
Message from goddess:
http://markmail.org/message/ruqp2t6gvhnhv654
So you better leave it off. It could open a full can of worms with process management. And there is nothing worse as misbehaving runaway native process sucking processor cycles and battery juice. Better solution would be several android abstractions like services and broadcast receivers in same process passing data around by means of static classes or firing intents
What is the advantage of using Binder for IPC over (Semaphores , Message Queue, PIPES) in Android stack?
Old question (and likely unmonitored by the poster), but worth answering:
A) All filesystem-based or filesystem-representable IPC mechanisms (notably pipes), can't be used because of a lack of a world-writable directory, where all processes can mkfifo/create the filesystem/socket representation of their IPC port (/dev/socket notwithstanding, which is used for system processes, e.g. rile, zygote, and their ilk).
B) None of the suggested mechanisms have the capability of "service location" which is required for Android. In UNIX, there's an RPC portmapper, and Android needs similar functionality. Enter: The ServiceManager, which can use binder to register as a context manager, to register/lookup service handles on the fly
C) There is an extensive need for serialization - be it intents, or other messages. Binder provides the parcel abstraction, which can be used for data marshaling by the Parcel.java.
D) SysV has other issues than Mr. Lambada's answer which are more paramount, notably race conditions, and lack of authorization.
E) Message queues and pipes can't pass descriptors. UNIX Domain sockets may, but can't be used because of (A) (again, unless you're root/system, like zygote, rild, installd..)
F) Binder is really lightweight, and has built-in authorization mechanisms. It also has nifty features like waking up the recipient process, as well as memory sharing, which the other mechanisms simply don't have. (and remember, no mmap(2), because of the file problem in (A) for named mappings).
and - let's not forget
G) Binder was started at Palm (ah, nostalgia) (q.v. OpenBinder). Ex-palmers got to Android, and brought their code in with them.
From the ndk's docs/system/libc/SYSV-IPC.html file:
Android does not support System V IPCs, i.e. the facilities provided by the following standard Posix headers:
<sys/sem.h> /* SysV semaphores */
<sys/shm.h> /* SysV shared memory segments */
<sys/msg.h> /* SysV message queues */
<sys/ipc.h> /* General IPC definitions */
The reason for this is due to the fact that, by design, they lead to global kernel resource leakage.
For example, there is no way to automatically release a SysV semaphore allocated in the kernel when:
a buggy or malicious process exits
a non-buggy and non-malicious process crashes or is explicitly killed.
Killing processes automatically to make room for new ones is an important part of Android's application lifecycle implementation. This means
that, even assuming only non-buggy and non-malicious code, it is very likely that over time, the kernel global tables used to implement SysV IPCs will fill
up.
At that point, strange failures are likely to occur and prevent programs that use them to run properly until the next reboot of the system.
Binders are used to to communicate over process boundaries since different processes don't share a common VM context => no more direct access to each others Objects (memory). Both parties within the same process (usually things that are within the same app) means (imho) that you should not use Binders since they slow down / complexify things unnecessary.
Binders are usually not used directly but rather via the "Service" or the "Messenger" classes. While communication with a Service is done via a full api of functions, the communication with a Messenger has to use "Message"s. Messengers a lot simpler to implement.
Apart from using Binders you can use anything that is available from any VM instance like "LocalSocket"s, Files, ContentProviders, Intents, ...
Binders are not ideal for transferring large data streams (like audio/video) since every object has to be converted to (and back from) a Parcel. All the conversion takes time. Much better in that case would be a LocalSocket for example.
Binders are used to enable remote procedure calls. You could implement RPC using the synchronization tools you mention but you would also need to write a lot of code to make it come together... with a Binder (normally only used within an Android Service) you have much less code to write; barely more than your actual remote functions.