DSU service: Pipeline the installation task to improve performance
Right now the installation task does roughly this: while (has partition data left) { a. Copy partition data to write buffer. b. Copy write buffer to shared memory. c. Binder call submitFromAshmem() to inform the consumer of the shared memory. } Both task (a) and (c) are I/O intensive and time consuming operations. However (a) and (c) don't have a strong data dependency, and the only consistency condition we need to maintain is "task (b) can only be started once task (c) from the previous iteration is complete." As soon as (b) is complete, (c) and *(a) of the next iteration* can be started, pipelining task (a) & (c). Also enlarge the default shared memory size because there are new improvements after this change. The new default size (512K) is chosen somewhat randomly, it's large enough to optimize the installation time and small enough to not starve most devices' RAM. Speedup: * physical device: 23s -> 18s (14s if shared memory buffer is 512K) * virtual device: 19s -> 15s Bug: 225310919 Test: Install and boot DSU Change-Id: If7093919762861d19d4fecaf997a699cc1b0fe41
This commit is contained in:
parent
952326b9cf
commit
8d629110c4
@ -41,6 +41,10 @@ import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
@ -51,8 +55,8 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
private static final String TAG = "InstallationAsyncTask";
|
||||
|
||||
private static final int MIN_SHARED_MEMORY_SIZE = 8 << 10; // 8KiB
|
||||
private static final int MAX_SHARED_MEMORY_SIZE = 1024 << 10; // 1MiB
|
||||
private static final int DEFAULT_SHARED_MEMORY_SIZE = 64 << 10; // 64KiB
|
||||
private static final int MAX_SHARED_MEMORY_SIZE = 8 << 20; // 8MiB
|
||||
private static final int DEFAULT_SHARED_MEMORY_SIZE = 512 << 10; // 512KiB
|
||||
private static final String SHARED_MEMORY_SIZE_PROP =
|
||||
"dynamic_system.data_transfer.shared_memory.size";
|
||||
|
||||
@ -488,7 +492,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
installWritablePartition("userdata", mUserdataSize);
|
||||
}
|
||||
|
||||
private void installImages() throws IOException, ImageValidationException {
|
||||
private void installImages() throws ExecutionException, IOException, ImageValidationException {
|
||||
if (mStream != null) {
|
||||
if (mIsZip) {
|
||||
installStreamingZipUpdate();
|
||||
@ -500,7 +504,8 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
}
|
||||
}
|
||||
|
||||
private void installStreamingGzUpdate() throws IOException, ImageValidationException {
|
||||
private void installStreamingGzUpdate()
|
||||
throws ExecutionException, IOException, ImageValidationException {
|
||||
Log.d(TAG, "To install a streaming GZ update");
|
||||
installImage("system", mSystemSize, new GZIPInputStream(mStream));
|
||||
}
|
||||
@ -528,7 +533,8 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
return total;
|
||||
}
|
||||
|
||||
private void installStreamingZipUpdate() throws IOException, ImageValidationException {
|
||||
private void installStreamingZipUpdate()
|
||||
throws ExecutionException, IOException, ImageValidationException {
|
||||
Log.d(TAG, "To install a streaming ZIP update");
|
||||
|
||||
ZipInputStream zis = new ZipInputStream(mStream);
|
||||
@ -548,7 +554,8 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
}
|
||||
}
|
||||
|
||||
private void installLocalZipUpdate() throws IOException, ImageValidationException {
|
||||
private void installLocalZipUpdate()
|
||||
throws ExecutionException, IOException, ImageValidationException {
|
||||
Log.d(TAG, "To install a local ZIP update");
|
||||
|
||||
Enumeration<? extends ZipEntry> entries = mZipFile.entries();
|
||||
@ -569,7 +576,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
}
|
||||
|
||||
private void installImageFromAnEntry(ZipEntry entry, InputStream is)
|
||||
throws IOException, ImageValidationException {
|
||||
throws ExecutionException, IOException, ImageValidationException {
|
||||
String name = entry.getName();
|
||||
|
||||
Log.d(TAG, "ZipEntry: " + name);
|
||||
@ -581,7 +588,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
}
|
||||
|
||||
private void installImage(String partitionName, long uncompressedSize, InputStream is)
|
||||
throws IOException, ImageValidationException {
|
||||
throws ExecutionException, IOException, ImageValidationException {
|
||||
|
||||
SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
|
||||
|
||||
@ -637,27 +644,51 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
|
||||
long prevInstalledSize = 0;
|
||||
long installedSize = 0;
|
||||
byte[] bytes = new byte[memoryFile.length()];
|
||||
int numBytesRead;
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
Future<Boolean> submitPromise = null;
|
||||
|
||||
while (true) {
|
||||
final int numBytesRead = sis.read(bytes, 0, bytes.length);
|
||||
|
||||
if (submitPromise != null) {
|
||||
// Wait until the previous submit task is complete.
|
||||
while (true) {
|
||||
try {
|
||||
if (!submitPromise.get()) {
|
||||
throw new IOException("Failed submitFromAshmem() to DynamicSystem");
|
||||
}
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
// Publish the progress of the previous submit task.
|
||||
if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
|
||||
publishProgress(installedSize);
|
||||
prevInstalledSize = installedSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the previous submit task (submitPromise) is complete before exiting the loop.
|
||||
if (numBytesRead < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
while ((numBytesRead = sis.read(bytes, 0, bytes.length)) != -1) {
|
||||
if (isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
|
||||
submitPromise =
|
||||
executor.submit(() -> mInstallationSession.submitFromAshmem(numBytesRead));
|
||||
|
||||
if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
|
||||
throw new IOException("Failed write() to DynamicSystem");
|
||||
}
|
||||
|
||||
// Even though we update the bytes counter here, the actual progress is updated only
|
||||
// after the submit task (submitPromise) is complete.
|
||||
installedSize += numBytesRead;
|
||||
|
||||
if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
|
||||
publishProgress(installedSize);
|
||||
prevInstalledSize = installedSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure a 100% mark is published.
|
||||
if (prevInstalledSize != partitionSize) {
|
||||
publishProgress(partitionSize);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user