Skip to content

Commit

Permalink
DirectApkSoSource: optimize ZipEntry lookup
Browse files Browse the repository at this point in the history
Summary:
DirectApkSoSource.loadLibrary() has quadratic behavior: for every library it
loads (either directly or as a dependency), it iterates over ZipFile entries
to find a match.

This patch replaces the linear search with ZipFile#getEntry. This results in
an ~8x startup improvement in a large app that loads ~1k shared libraries.

This can be optimized further if needed, by potentially trading some memory
(reusing open ZipFile objects) or doing more work up front (computing all
the dependencies on startup) for faster lookups.

Reviewed By: simpleton

Differential Revision: D54651944

fbshipit-source-id: 5d0fc8fb31decfc3bd246a7c02a342c25a5f9427
  • Loading branch information
tnovak authored and facebook-github-bot committed Mar 8, 2024
1 parent 57f2556 commit f5a31ef
Showing 1 changed file with 17 additions and 8 deletions.
25 changes: 17 additions & 8 deletions java/com/facebook/soloader/DirectApkSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ protected void prepare(int flags) throws IOException {
}

private void prepare() throws IOException {
String subDir = null;
for (String directApkLdPath : mDirectApkLdPaths) {
String subDir = null;
if (!TextUtils.isEmpty(directApkLdPath)) {
final int i = directApkLdPath.indexOf('!');
if (i >= 0 && i + 2 < directApkLdPath.length()) {
Expand Down Expand Up @@ -203,14 +203,18 @@ private void prepare() throws IOException {
}

private void buildLibDepsCache(String directApkLdPath, String soName) throws IOException {
try (ZipFile mZipFile = new ZipFile(getApkPathFromLdPath(directApkLdPath))) {
Enumeration<? extends ZipEntry> entries = mZipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry != null && entry.getName().endsWith("/" + soName)) {
buildLibDepsCacheImpl(directApkLdPath, mZipFile, entry, soName);
}
final String apkPath = getApkPathFromLdPath(directApkLdPath);

try (ZipFile mZipFile = new ZipFile(apkPath)) {
final String path = getLibraryPathInApk(directApkLdPath, soName);
final ZipEntry entry = mZipFile.getEntry(path);

if (entry == null) {
LogUtil.e(SoLoader.TAG, path + " not found in " + apkPath);
return;
}

buildLibDepsCacheImpl(directApkLdPath, mZipFile, entry, soName);
}
}

Expand Down Expand Up @@ -273,6 +277,11 @@ private static String getApkPathFromLdPath(String ldPath) {
return ldPath.substring(0, ldPath.indexOf('!'));
}

private static String getLibraryPathInApk(String directApkLdPath, String soName) {
final int index = directApkLdPath.indexOf('!');
return directApkLdPath.substring(index + 2) + File.separator + soName;
}

@Override
public SoSource recover(Context context) {
DirectApkSoSource recovered = new DirectApkSoSource(context);
Expand Down

0 comments on commit f5a31ef

Please sign in to comment.