Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 49 additions & 31 deletions src/commands/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clap::Parser;
use clap::{Parser, ValueEnum};
use std::{path::PathBuf, time::Duration};
use tracing::error;
use url::Url;
Expand All @@ -11,6 +11,7 @@ pub const ENV_REQUIRE_OPTIMAL: &str = "STRIDE_OPTIMAL";
pub const ENV_KEEP_LOGS: &str = "STRIDE_KEEP";
pub const ENV_STRIDE_MAX_RUN_LOGS: &str = "STRIDE_MAX_RUN_LOGS";
pub const ENV_STRIDE_SERVER: &str = "STRIDE_SERVER";
pub const ENV_INSTANCE_ORDER: &str = "STRIDE_INSTANCE_ORDER";
pub const STRIDE_SERVER_DEFAULT: &str = "https://pace2026.imada.sdu.dk/";
pub const ENV_STRIDE_DOWNLOADS_PATH: &str = "STRIDE_DOWNLOADS_PATH";
pub const STRIDE_DOWNLOADS_PATH_DEFAULT: &str = "stride-downloads";
Expand Down Expand Up @@ -70,35 +71,30 @@ pub struct CommandCheckArgs {
pub upload: bool,
}

#[derive(Clone, Copy, Debug, ValueEnum)]
pub enum InstanceOrder {
#[value(alias = "rand", alias = "r")]
Random,
#[value(alias = "nta")]
NodesTreesAsc,
#[value(alias = "ntd")]
NodesTreesDesc,
#[value(alias = "tna")]
TreesNodesAsc,
#[value(alias = "tnd")]
TreesNodesDesc,
}

#[derive(Parser, Debug, Clone)]
pub struct CommandRunArgs {
#[arg(short, long, env = ENV_SOLVER, help = "Solver program to execute")]
pub solver: PathBuf,

#[arg(short, long, help = "List of instance files", required = true, num_args(1..))]
pub instances: Vec<PathBuf>,

#[arg(short='t', long="timeout", env = ENV_SOFT_TIMEOUT, value_parser = parse_duration, help = "Solver time budget in seconds (then SIGTERM)", default_value="30")]
pub soft_timeout: Duration,
#[arg(long, env=ENV_STRIDE_DOWNLOADS_PATH, help="Path where downloads from STRIDE server are placed.", default_value = STRIDE_DOWNLOADS_PATH_DEFAULT)]
pub downloads_path: PathBuf,

#[arg(short='g', long="grace", env = ENV_GRACE_PERIOD, value_parser = parse_duration, help = "Seconds between SIGTERM and SIGKILL", default_value="5")]
pub grace_period: Duration,

#[arg(
short = 'p',
long = "parallel",
env = ENV_PARALLEL_JOBS,
help = "Number of solvers to run in parallel; default: number of physical cores"
)]
pub parallel_jobs: Option<u64>,

#[arg(
short = 'o',
long = "optimal",
env = ENV_REQUIRE_OPTIMAL,
help = "Treat suboptimal solutions as error, e.g. keep logs of suboptimal runs"
)]
pub require_optimal: bool,
#[arg(short, long, help = "List of instance files", required = true, num_args(1..))]
pub instances: Vec<PathBuf>,

#[arg(
short = 'k',
Expand All @@ -122,20 +118,42 @@ pub struct CommandRunArgs {
)]
pub no_envs: bool,

#[arg(last = true, help = "Arguments passed to solver")]
pub solver_args: Vec<String>,

#[arg(short = 'S', long, env = ENV_STRIDE_SERVER, default_value = STRIDE_SERVER_DEFAULT, help = "Server to upload to")]
pub stride_server: Url,
#[arg(short = 'x', value_enum, default_value_t = InstanceOrder::Random, help = "Order in which instances are executed", env = ENV_INSTANCE_ORDER)]
pub order: InstanceOrder,

#[arg(short = 'O', long, help = "Do not communicate with STRIDE servers")]
pub offline: bool,

#[arg(
short = 'p',
long = "parallel",
env = ENV_PARALLEL_JOBS,
help = "Number of solvers to run in parallel; default: number of physical cores"
)]
pub parallel_jobs: Option<u64>,

#[arg(short = 'r', long="max_run_logs", env = ENV_STRIDE_MAX_RUN_LOGS, help="If more run logs are in the stride-log dir, remove oldest ones")]
pub remove_old_logs: Option<usize>,

#[arg(long, env=ENV_STRIDE_DOWNLOADS_PATH, help="Path where downloads from STRIDE server are placed.", default_value = STRIDE_DOWNLOADS_PATH_DEFAULT)]
pub downloads_path: PathBuf,
#[arg(
short = 'o',
long = "optimal",
env = ENV_REQUIRE_OPTIMAL,
help = "Treat suboptimal solutions as error, e.g. keep logs of suboptimal runs"
)]
pub require_optimal: bool,

#[arg(short = 'S', long, env = ENV_STRIDE_SERVER, default_value = STRIDE_SERVER_DEFAULT, help = "Server to upload to")]
pub stride_server: Url,

#[arg(short='t', long="timeout", env = ENV_SOFT_TIMEOUT, value_parser = parse_duration, help = "Solver time budget in seconds (then SIGTERM)", default_value="30")]
pub soft_timeout: Duration,

#[arg(short, long, env = ENV_SOLVER, help = "Solver program to execute")]
pub solver: PathBuf,

#[arg(last = true, help = "Arguments passed to solver")]
pub solver_args: Vec<String>,
}

#[derive(Parser, Debug, Clone)]
Expand Down
8 changes: 4 additions & 4 deletions src/commands/run/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use thiserror::Error;
use tracing::{error, trace};

use crate::commands::run::upload::{JobResultUploadAggregation, UploadError, UploadToStride};
use crate::instances::instance::{InstanceError, collect_instances};
use crate::instances::instance::{InstanceError, collect_instances, sort_instances};
use crate::instances::parser::InstanceSourceParser;
use crate::instances::{
directory::InstanceDirectory, instance::Instance, parser::collect_instances_from_args,
Expand All @@ -29,7 +29,7 @@ use tokio::time::timeout;
use tokio::time::{Duration, sleep};
use tracing_subscriber::EnvFilter;

const DISPLAY_TICK_MIN_WAIT: Duration = Duration::from_millis(25);
const DISPLAY_TICK_MIN_WAIT: Duration = Duration::from_millis(250);

pub async fn command_run(args: &CommandRunArgs) -> Result<(), CommandRunError> {
let mut task_context = TaskContext::new(args.clone()).await?;
Expand All @@ -39,6 +39,8 @@ pub async fn command_run(args: &CommandRunArgs) -> Result<(), CommandRunError> {

let mut instances =
collect_instances(&instance_dir, collect_instances_from_args(&args.instances)?)?;
sort_instances(&mut instances, args.order);

let instances_with_digest = instances.iter().filter(|i| i.idigest().is_some()).count();

task_context.display.set_total_instance(instances.len());
Expand Down Expand Up @@ -113,11 +115,9 @@ pub async fn command_run(args: &CommandRunArgs) -> Result<(), CommandRunError> {
}
}

sleep(DISPLAY_TICK_MIN_WAIT).await;
task_context.display.post_processing_tick();
sleep(DISPLAY_TICK_MIN_WAIT).await;
task_context.display.final_message();
sleep(DISPLAY_TICK_MIN_WAIT).await;

Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions src/commands/run/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ impl ProgressDisplay {
}

pub fn final_message(&self) {
self.stride_line.finish();
self.status_line.finish();
println!("{}", self.status_line.message());
}

Expand Down
44 changes: 44 additions & 0 deletions src/instances/instance.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::commands::arguments::InstanceOrder;
use crate::instances::directory::InstanceDirectory;
use crate::instances::parser::{InstanceSource, InstanceSourceDescriptor};
use console::Style;
use pace26checker::digest::digest_output::InstanceDigest;
use pace26io::pace::reader::{Action, InstanceReader, InstanceVisitor, ReaderError};
use rand::prelude::SliceRandom;
use std::cmp::Reverse;
use std::collections::HashMap;
use std::io::{BufReader, ErrorKind};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -230,6 +233,47 @@ fn dedup_instances(instances: &mut Vec<Instance>) {
}
}

pub fn sort_instances(instances: &mut [Instance], order: InstanceOrder) {
// we later pop the instances from the back; hence the order here is flipped!
match order {
InstanceOrder::Random => {
instances.shuffle(&mut rand::rng());
}
InstanceOrder::NodesTreesAsc => {
instances.sort_unstable_by_key(|i| {
Reverse((
i.num_leaves.unwrap_or(usize::MAX),
i.num_trees.unwrap_or(usize::MAX),
))
});
}
InstanceOrder::NodesTreesDesc => {
instances.sort_unstable_by_key(|i| {
(
i.num_leaves.unwrap_or(usize::MAX),
i.num_trees.unwrap_or(usize::MAX),
)
});
}
InstanceOrder::TreesNodesAsc => {
instances.sort_unstable_by_key(|i| {
Reverse((
i.num_trees.unwrap_or(usize::MAX),
i.num_leaves.unwrap_or(usize::MAX),
))
});
}
InstanceOrder::TreesNodesDesc => {
instances.sort_unstable_by_key(|i| {
(
i.num_trees.unwrap_or(usize::MAX),
i.num_leaves.unwrap_or(usize::MAX),
)
});
}
}
}

#[derive(Default)]
struct InstanceHeaderVisitor {
idigest: Option<InstanceDigest>,
Expand Down
2 changes: 1 addition & 1 deletion testcases/test_solver_valid/shortwait.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#s test_params {"print": "((5,((3,1),2)),6);\n4;", "wait_seconds": 0.8}
#s test_params {"print": "((5,((3,1),2)),6);\n4;", "wait_seconds": 0.9}
#p 2 6
((5,(((3,1),4),2)),6);
(6,(((1,3),2),(4,5)));
2 changes: 1 addition & 1 deletion tests/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async fn profile_time_shortwait() {
assert_eq!(result, JobResult::Valid { size: 2 });

assert!(
infos.get("s_wtime").unwrap().as_f64().unwrap() > 0.7,
infos.get("s_wtime").unwrap().as_f64().unwrap() > 0.65,
"actual: {:?}",
infos.get("s_wtime")
);
Expand Down
Loading