Optimize to use decode buffer pool (#1890)

* Optimize to use decode buffer poll

* Drop unnecessary logging

* Refine

* Update app/src/main/cpp/archive.c

Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>

---------

Co-authored-by: Dude so hot <djohn@fbi.gov>
Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2024-11-05 05:01:15 +00:00
1 parent 5f2f3f9122
commit a566393a89
1 file changed
+49 -57
+49 -57
View File
@@ -49,28 +49,21 @@ typedef struct {
void *addr;
} entry;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static archive_ctx **ctx_pool = NULL;
#define CTX_POOL_SIZE 20
#define MAX_PARALLEL_DECOMP 4
#define max(a, b) ((a) > (b) ? (a) : (b))
static void *mempool = MAP_FAILED;
static size_t *mempoolofs = NULL;
static int page_size = 0;
#define PAGE_ALIGN(x) ((x + page_size - 1) & ~(page_size - 1))
#define MEMPOOL_ADDR_BY_SORTED_IDX(x) (mempool + (index ? mempoolofs[index - 1] : 0))
#define MEMPOOL_SIZE (mempoolofs[entryCount - 1])
#define PROT_RW (PROT_WRITE | PROT_READ)
#define MAP_ANON_POOL (MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE)
static pthread_mutex_t ctx_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
static archive_ctx **ctx_pool = NULL;
static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
static void *decode_buffer[MAX_PARALLEL_DECOMP];
static bool need_encrypt = false;
static char *passwd = NULL;
static void *archiveAddr = MAP_FAILED;
static size_t archiveSize = 0;
static entry *entries = NULL;
static size_t entryCount = 0;
static ssize_t max_file_size = 0;
const char supportExt[10][6] = {
"jpeg",
@@ -130,7 +123,7 @@ static bool fill_entry_zero_copy(struct archive *arc, entry *entry) {
return zero_copy;
}
static bool archive_map_entries_index(archive_ctx *ctx, bool sort) {
static void archive_map_entries_index(archive_ctx *ctx, bool sort) {
int count = 0;
bool zero_copy = true;
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK) {
@@ -138,40 +131,44 @@ static bool archive_map_entries_index(archive_ctx *ctx, bool sort) {
if (archive_entry_is_file(ctx->entry) && filename_is_playable_file(name)) {
entries[count].filename = strdup(name);
entries[count].index = count;
entries[count].size = archive_entry_size(ctx->entry);
ssize_t size = archive_entry_size(ctx->entry);
max_file_size = max(size, max_file_size);
entries[count].size = size;
// We don't expect zero copy if first content can't do zero copy
if (zero_copy) zero_copy = fill_entry_zero_copy(ctx->arc, &entries[count]);
count++;
}
}
if (sort) qsort(entries, entryCount, sizeof(entry), compare_entries);
return zero_copy;
}
static void archive_prealloc_mempool() {
mempoolofs = calloc(entryCount, sizeof(size_t));
for (int i = 0; i < entryCount; ++i) {
mempoolofs[i] = PAGE_ALIGN(entries[i].size) + (i ? mempoolofs[i - 1] : 0);
}
mempool = mmap(0, MEMPOOL_SIZE, PROT_RW, MAP_ANON_POOL, -1, 0);
if (mempool == MAP_FAILED) {
LOGE("%s%s", "mmap failed with error ", strerror(errno));
abort();
static void *acquire_decode_buffer() {
void *addr = NULL;
pthread_mutex_lock(&buffer_mutex);
for (int i = 0; i < MAX_PARALLEL_DECOMP; ++i) {
addr = decode_buffer[i];
if (addr) {
decode_buffer[i] = NULL;
break;
}
}
pthread_mutex_unlock(&buffer_mutex);
if (!addr) addr = malloc(max_file_size);
return addr;
}
static void mempool_release_pages(void *addr, size_t size) {
size = PAGE_ALIGN(size);
madvise_log_if_error(addr, size, MADV_DONTNEED);
}
static bool kernel_can_prefault = true;
static void mempool_prefault_pages(void *addr, size_t size) {
size = PAGE_ALIGN(size);
if (kernel_can_prefault) {
if (madvise(addr, size, MADV_POPULATE_WRITE)) kernel_can_prefault = false;
static void release_decode_buffer(void *buffer) {
pthread_mutex_lock(&buffer_mutex);
for (int i = 0; i < MAX_PARALLEL_DECOMP; ++i) {
void *addr = decode_buffer[i];
if (!addr) {
decode_buffer[i] = buffer;
pthread_mutex_unlock(&buffer_mutex);
return;
}
}
pthread_mutex_unlock(&buffer_mutex);
free(buffer);
}
static int archive_list_all_entries(archive_ctx *ctx) {
@@ -227,7 +224,7 @@ static int archive_skip_to_index(archive_ctx *ctx, int index) {
static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
int ret;
archive_ctx *ctx = NULL;
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&ctx_pool_mutex);
for (int i = 0; i < CTX_POOL_SIZE; i++) {
if (!ctx_pool[i])
continue;
@@ -242,14 +239,14 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
}
if (ctx)
ctx->using = 1;
pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&ctx_pool_mutex);
if (!ctx) {
archive_ctx *victimCtx = NULL;
int victimIdx = 0;
int replace = 1;
ctx = archive_alloc_ctx();
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&ctx_pool_mutex);
for (int i = 0; i < CTX_POOL_SIZE; i++) {
if (!ctx_pool[i]) {
ctx_pool[i] = ctx;
@@ -264,7 +261,7 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
}
}
if (replace) ctx_pool[victimIdx] = ctx;
pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&ctx_pool_mutex);
if (replace) archive_release_ctx(victimCtx);
}
ret = archive_skip_to_index(ctx, idx);
@@ -282,7 +279,6 @@ JNIEXPORT jint JNICALL
Java_com_hippo_ehviewer_jni_ArchiveKt_openArchive(JNIEnv *env, jclass thiz, jint fd, jlong size, jboolean sort_entries) {
EH_UNUSED(env);
EH_UNUSED(thiz);
page_size = getpagesize();
archive_ctx *ctx = NULL;
archiveAddr = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (archiveAddr == MAP_FAILED) {
@@ -327,10 +323,8 @@ Java_com_hippo_ehviewer_jni_ArchiveKt_openArchive(JNIEnv *env, jclass thiz, jint
ctx = archive_alloc_ctx();
entries = calloc(entryCount, sizeof(entry));
bool fully_zero_copy = archive_map_entries_index(ctx, sort_entries);
archive_map_entries_index(ctx, sort_entries);
archive_release_ctx(ctx);
if (!fully_zero_copy) archive_prealloc_mempool();
return (int) entryCount;
}
@@ -343,11 +337,9 @@ Java_com_hippo_ehviewer_jni_ArchiveKt_extractToByteBuffer(JNIEnv *env, jclass th
if (entry->addr) {
return (*env)->NewDirectByteBuffer(env, entry->addr, size);
} else {
void *addr = MEMPOOL_ADDR_BY_SORTED_IDX(index);
index = entry->index;
archive_ctx *ctx = NULL;
if (!archive_get_ctx(&ctx, index)) {
mempool_prefault_pages(addr, size);
if (!archive_get_ctx(&ctx, entry->index)) {
void *addr = acquire_decode_buffer();
ssize_t bytes = archive_read_data(ctx->arc, addr, size);
ctx->using = 0;
if (bytes == size) {
@@ -359,7 +351,7 @@ Java_com_hippo_ehviewer_jni_ArchiveKt_extractToByteBuffer(JNIEnv *env, jclass th
LOGE("%s", "No enough data read, WTF?");
}
}
mempool_release_pages(addr, size);
release_decode_buffer(addr);
}
}
return 0;
@@ -382,12 +374,11 @@ Java_com_hippo_ehviewer_jni_ArchiveKt_closeArchive(JNIEnv *env, jclass thiz) {
munmap(archiveAddr, archiveSize);
archiveAddr = MAP_FAILED;
}
if (mempool != MAP_FAILED) {
munmap(mempool, MEMPOOL_SIZE);
mempool = MAP_FAILED;
for (int i = 0; i < MAX_PARALLEL_DECOMP; ++i) {
free(decode_buffer[i]);
decode_buffer[i] = NULL;
}
free(mempoolofs);
mempoolofs = NULL;
max_file_size = 0;
if (entries) {
for (int i = 0; i < entryCount; ++i) {
free((void *) entries[i].filename);
@@ -458,8 +449,9 @@ JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_jni_ArchiveKt_releaseByteBuffer(JNIEnv *env, jclass thiz, jobject buffer) {
EH_UNUSED(thiz);
void *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
if (!ADDR_IN_FILE_MAPPING(addr)) mempool_release_pages(addr, size);
if (!ADDR_IN_FILE_MAPPING(addr)) {
release_decode_buffer(addr);
}
}
JNIEXPORT void JNICALL