Skip to content

Commit

Permalink
perf: update_integrations multi-threading
Browse files Browse the repository at this point in the history
  • Loading branch information
krypt0nn committed Jan 10, 2024
1 parent 122259c commit 9cd616a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 32 deletions.
2 changes: 1 addition & 1 deletion perf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cargo build --profile profiling

# Generate perf.data and flamechart for the app
# cargo install flamegraph
flamegraph --flamechart -F 2000 -- ./target/profiling/anime-games-launcher
flamegraph --flamechart -- ./target/profiling/anime-games-launcher

# Format perf.data to the profiler.firefox.com compatible format
perf script -F +pid > perf-output.data
12 changes: 4 additions & 8 deletions src/ui/windows/loading/check_addons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,7 @@ fn get_game_addons(
}

#[inline]
pub fn check_addons() -> anyhow::Result<Vec<AddonsListEntry>> {
let pool = rusty_pool::Builder::new()
.name(String::from("check_addons"))
.build();

pub fn check_addons(pool: &rusty_pool::ThreadPool) -> anyhow::Result<Vec<AddonsListEntry>> {
let config = config::get();

let mut tasks = Vec::new();
Expand All @@ -120,19 +116,19 @@ pub fn check_addons() -> anyhow::Result<Vec<AddonsListEntry>> {

for edition in game.get_game_editions_list()? {
let enabled_addons = &settings.addons[&edition.name];

let game_info = CardInfo::Game {
name: game.manifest.game_name.clone(),
title: game.manifest.game_title.clone(),
developer: game.manifest.game_developer.clone(),
picture_uri: game.get_card_picture(&edition.name)?,
edition: edition.name.clone()
};

let game_addons = get_game_addons(&game_info, game, &edition.name, enabled_addons)?
.into_iter()
.flatten();

addons.extend(game_addons);
}

Expand Down
11 changes: 9 additions & 2 deletions src/ui/windows/loading/load_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub struct LoadingResult {
}

pub fn load_app(sender: &ComponentSender<LoadingApp>) -> Result<LoadingResult, LoadingAppMsg> {
let pool = rusty_pool::Builder::new()
.name(String::from("load_app"))
.build();

let begin = std::time::Instant::now();

sender.input(LoadingAppMsg::SetProgress(0.0));
Expand All @@ -41,7 +45,7 @@ pub fn load_app(sender: &ComponentSender<LoadingApp>) -> Result<LoadingResult, L
sender.input(LoadingAppMsg::SetProgress(2.0 / TOTAL_STEPS));
sender.input(LoadingAppMsg::SetActiveStage(String::from("Updating integration scripts")));

update_integrations::update_integrations().map_err(|err| LoadingAppMsg::DisplayError {
update_integrations::update_integrations(&pool).map_err(|err| LoadingAppMsg::DisplayError {
title: String::from("Failed to update integration scripts"),
message: err.to_string()
})?;
Expand Down Expand Up @@ -94,13 +98,16 @@ pub fn load_app(sender: &ComponentSender<LoadingApp>) -> Result<LoadingResult, L
sender.input(LoadingAppMsg::SetProgress(9.0 / TOTAL_STEPS));
sender.input(LoadingAppMsg::SetActiveStage(String::from("Checking games addons")));

let download_addons = check_addons::check_addons().map_err(|err| LoadingAppMsg::DisplayError {
let download_addons = check_addons::check_addons(&pool).map_err(|err| LoadingAppMsg::DisplayError {
title: String::from("Failed to check games addons"),
message: err.to_string()
})?;

sender.input(LoadingAppMsg::SetProgress(1.0));

// TODO: pulse progress bar before it's joined
pool.join();

println!("Launcher loading time: {} ms", begin.elapsed().as_millis());

Ok(LoadingResult {
Expand Down
74 changes: 53 additions & 21 deletions src/ui/windows/loading/update_integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,72 @@ use anime_game_core::network::minreq;
use crate::config;
use crate::games::integrations::manifest::Manifest;

// TODO: parallelize this
struct IntegrationInfo {
pub source: String,
pub manifest_body: Vec<u8>,
pub manifest: Manifest
}

#[inline]
pub fn update_integrations() -> anyhow::Result<()> {
pub fn update_integrations(pool: &rusty_pool::ThreadPool) -> anyhow::Result<()> {
let config = config::get();

let mut games = HashMap::new();
let mut tasks = Vec::with_capacity(config.games.integrations.sources.len());

for source in config.games.integrations.sources {
let integrations = minreq::get(format!("{source}/integrations.json"))
.send()?.json::<Json>()?;
tasks.push(pool.evaluate(move || -> anyhow::Result<HashMap<String, IntegrationInfo>> {
let integrations = minreq::get(format!("{source}/integrations.json"))
.send()?.json::<Json>()?;

let Some(integrations) = integrations.get("games").and_then(Json::as_array) else {
anyhow::bail!("Wrong integrations file structue");
};

let Some(integrations) = integrations.get("games").and_then(Json::as_array) else {
anyhow::bail!("Wrong integrations file structue");
};
let mut games = HashMap::new();

for game in integrations {
if let Some(game) = game.as_str() {
let bytes = minreq::get(format!("{source}/games/{game}/manifest.json"))
.send()?.into_bytes();
for game in integrations {
if let Some(game) = game.as_str() {
let bytes = minreq::get(format!("{source}/games/{game}/manifest.json"))
.send()?.into_bytes();

let manifest = Manifest::from_json(&serde_json::from_slice(&bytes)?)?;
let manifest = Manifest::from_json(&serde_json::from_slice(&bytes)?)?;

games.insert(game.to_string(), (source.clone(), manifest, bytes));
games.insert(game.to_string(), IntegrationInfo {
source: source.clone(),
manifest_body: bytes,
manifest
});
}
}

Ok(games)
}));
}

let mut games = HashMap::new();

for task in tasks {
for (game, value) in task.await_complete()? {
games.insert(game, value);
}
}

for (game, (source, manifest, bytes)) in games {
let mut tasks = Vec::with_capacity(games.len());

for (game, info) in games {
let integration_path = config.games.integrations.path.join(&game);

let manifest_path = integration_path.join("manifest.json");
let script_path = integration_path.join(&manifest.script_path);
let script_path = integration_path.join(&info.manifest.script_path);

// Spawning new threads to read a few KBs of data is more time-consuming
// than doing it in the same thread
if integration_path.exists() {
let local_manifest = std::fs::read(&manifest_path)?;
let local_manifest = serde_json::from_slice(&local_manifest)?;
let local_manifest = Manifest::from_json(&local_manifest)?;

if local_manifest.script_version == manifest.script_version {
if local_manifest.script_version == info.manifest.script_version {
continue;
}
}
Expand All @@ -55,12 +81,18 @@ pub fn update_integrations() -> anyhow::Result<()> {
std::fs::create_dir_all(&integration_path)?;
}

let script = minreq::get(format!("{source}/games/{game}/{}", &manifest.script_path))
.send()?.into_bytes();
tasks.push(pool.evaluate(move || -> anyhow::Result<()> {
let script = minreq::get(format!("{}/games/{game}/{}", info.source, &info.manifest.script_path))
.send()?.into_bytes();

std::fs::write(manifest_path, bytes)?;
std::fs::write(script_path, script)?;
std::fs::write(manifest_path, info.manifest_body)?;
std::fs::write(script_path, script)?;

Ok(())
}));
}

tasks.into_iter().try_for_each(|task| task.await_complete())?;

Ok(())
}

0 comments on commit 9cd616a

Please sign in to comment.