From cfb6238ad7fe7dd7d2eca64676437bb871802375 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 18 Feb 2022 17:59:28 +0100 Subject: Faster image loading/thumbnail creation by using multiple threads --- include/AsyncImageLoader.hpp | 5 ++-- include/AsyncTask.hpp | 4 +-- src/AsyncImageLoader.cpp | 66 +++++++++++++++++++++++--------------------- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/include/AsyncImageLoader.hpp b/include/AsyncImageLoader.hpp index 23c2889..c482a3a 100644 --- a/include/AsyncImageLoader.hpp +++ b/include/AsyncImageLoader.hpp @@ -41,6 +41,7 @@ namespace QuickMedia { // One example of why you might not want a symlink is if |thumbnail_path| is a temporary file. bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, mgl::vec2i resize_target_size, ContentType content_type, bool symlink_if_no_resize); + constexpr int NUM_IMAGE_DOWNLOAD_PARALLEL = 4; constexpr int NUM_IMAGE_LOAD_PARALLEL = 4; class AsyncImageLoader { @@ -85,8 +86,8 @@ namespace QuickMedia { private: std::mutex download_mutex; // TODO: Use curl single-threaded multi-download feature instead - Download downloads[NUM_IMAGE_LOAD_PARALLEL]; - AsyncTask load_thread; + Download downloads[NUM_IMAGE_DOWNLOAD_PARALLEL]; + AsyncTask load_threads[NUM_IMAGE_LOAD_PARALLEL]; MessageQueue image_thumbnail_create_queue; std::unordered_map> thumbnails; size_t counter = 0; diff --git a/include/AsyncTask.hpp b/include/AsyncTask.hpp index e256dc7..358e06a 100644 --- a/include/AsyncTask.hpp +++ b/include/AsyncTask.hpp @@ -20,14 +20,14 @@ namespace QuickMedia { thread = std::thread(&AsyncTask::thread_handler, this, std::move(promise), std::move(callback_func), std::forward(args)...); } - AsyncTask(AsyncTask &&other) { + AsyncTask(AsyncTask &&other) noexcept { cancel(); std::lock_guard lock(mutex); thread = std::move(other.thread); future = std::move(other.future); } - AsyncTask& operator=(AsyncTask &&other) { + AsyncTask& operator=(AsyncTask &&other) noexcept { cancel(); std::lock_guard lock(mutex); thread = std::move(other.thread); diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index e6d0b52..b0efb05 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -247,47 +247,29 @@ namespace QuickMedia { } AsyncImageLoader::AsyncImageLoader() { - for(int i = 0; i < NUM_IMAGE_LOAD_PARALLEL; ++i) { + for(int i = 0; i < NUM_IMAGE_DOWNLOAD_PARALLEL; ++i) { downloads[i].read_program.pid = -1; downloads[i].read_program.read_fd = -1; } - load_thread = AsyncTask([this]() mutable { - std::optional thumbnail_load_data_opt; - while(image_thumbnail_create_queue.is_running()) { - thumbnail_load_data_opt = image_thumbnail_create_queue.pop_if_available(); - if(thumbnail_load_data_opt) { + for(int i = 0; i < NUM_IMAGE_LOAD_PARALLEL; ++i) { + load_threads[i] = AsyncTask([this]() mutable { + std::optional thumbnail_load_data_opt; + while(image_thumbnail_create_queue.is_running()) { + thumbnail_load_data_opt = image_thumbnail_create_queue.pop_wait(); + if(!thumbnail_load_data_opt) + break; + // TODO: Do this multithreaded because creating thumbnails is pretty slow single-threaded, // especially video thumbnails. process_thumbnail(thumbnail_load_data_opt.value()); if(thumbnail_load_data_opt.value().thumbnail_data->loading_state == LoadingState::READY_TO_LOAD) load_processed_thumbnail(thumbnail_load_data_opt.value()); - thumbnail_load_data_opt = std::nullopt; - } - for(int i = 0; i < NUM_IMAGE_LOAD_PARALLEL; ++i) { - Download &download = downloads[i]; - if(download.read_program.pid == -1) - continue; - - int status = 0; - if(wait_program_non_blocking(download.read_program.pid, &status)) { - Path tmp_thumbnail_path = download.thumbnail_path; - tmp_thumbnail_path.append(".tmp"); - if(status == 0 && rename_atomic(tmp_thumbnail_path.data.c_str(), download.thumbnail_path.data.c_str()) == 0) { - fprintf(stderr, "Download duration for %s: %ld ms\n", download.url.c_str(), get_boottime_milliseconds() - download.download_start); - ThumbnailLoadData load_data = { std::move(download.url), std::move(download.thumbnail_path), false, download.thumbnail_data, download.resize_target_size }; - image_thumbnail_create_queue.push(std::move(load_data)); - } else { - fprintf(stderr, "Thumbnail download failed for %s\n", download.url.c_str()); - } - reset_download(download); - } + thumbnail_load_data_opt = std::nullopt; } - - std::this_thread::sleep_for(std::chrono::milliseconds(2)); - } - }); + }); + } } AsyncImageLoader::~AsyncImageLoader() { @@ -376,11 +358,31 @@ namespace QuickMedia { } void AsyncImageLoader::update() { + for(int i = 0; i < NUM_IMAGE_DOWNLOAD_PARALLEL; ++i) { + Download &download = downloads[i]; + if(download.read_program.pid == -1) + continue; + + int status = 0; + if(wait_program_non_blocking(download.read_program.pid, &status)) { + Path tmp_thumbnail_path = download.thumbnail_path; + tmp_thumbnail_path.append(".tmp"); + if(status == 0 && rename_atomic(tmp_thumbnail_path.data.c_str(), download.thumbnail_path.data.c_str()) == 0) { + fprintf(stderr, "Download duration for %s: %ld ms\n", download.url.c_str(), get_boottime_milliseconds() - download.download_start); + ThumbnailLoadData load_data = { std::move(download.url), std::move(download.thumbnail_path), false, download.thumbnail_data, download.resize_target_size }; + image_thumbnail_create_queue.push(std::move(load_data)); + } else { + fprintf(stderr, "Thumbnail download failed for %s\n", download.url.c_str()); + } + reset_download(download); + } + } + bool loaded_textures_changed = false; for(auto it = thumbnails.begin(); it != thumbnails.end();) { if(it->second->counter != counter) { { - for(int i = 0; i < NUM_IMAGE_LOAD_PARALLEL; ++i) { + for(int i = 0; i < NUM_IMAGE_DOWNLOAD_PARALLEL; ++i) { Download &download = downloads[i]; if(download.read_program.pid == -1) continue; @@ -409,7 +411,7 @@ namespace QuickMedia { } int AsyncImageLoader::get_free_load_index() const { - for(int i = 0; i < NUM_IMAGE_LOAD_PARALLEL; ++i) { + for(int i = 0; i < NUM_IMAGE_DOWNLOAD_PARALLEL; ++i) { if(downloads[i].read_program.pid == -1) return i; } -- cgit v1.2.3