From 89d86f9deb661e1afa0dd28b4c448f5ba71d190d Mon Sep 17 00:00:00 2001 From: FiveMovesAhead Date: Wed, 4 Jun 2025 11:06:26 +0100 Subject: [PATCH] Rework architecture so each challenge has separate runtime & docker. --- Dockerfile.dev | 29 ++- Dockerfile.runtime | 27 +-- scripts/download_algorithm | 14 +- scripts/list_algorithms | 12 +- scripts/test_algorithm | 15 +- tig-algorithms/Cargo.toml | 12 +- tig-algorithms/src/lib.rs | 19 +- tig-binary/Cargo.toml | 12 +- tig-binary/scripts/build_algorithm | 48 +++++ tig-binary/scripts/build_ptx | 19 +- tig-binary/scripts/build_so | 55 ++---- tig-challenges/Cargo.toml | 10 + tig-challenges/src/lib.rs | 19 +- tig-runtime/Cargo.toml | 12 +- tig-runtime/src/main.rs | 292 +++++++++++++++-------------- tig-verifier/Cargo.toml | 12 +- tig-verifier/src/main.rs | 155 ++++++++------- 17 files changed, 447 insertions(+), 315 deletions(-) create mode 100644 tig-binary/scripts/build_algorithm diff --git a/Dockerfile.dev b/Dockerfile.dev index 76dab15..8c49d30 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,8 +1,13 @@ ARG BASE_IMAGE=ubuntu:24.04 +ARG CHALLENGE # Development environment stage FROM ${BASE_IMAGE} +ARG CHALLENGE +ENV CHALLENGE=${CHALLENGE} +RUN if [ -z "$CHALLENGE" ]; then echo "Error: '--build-arg CHALLENGE' must be set." && exit 1; fi + RUN ARCH=$(uname -m) && \ if [ "$ARCH" != "aarch64" ] && [ "$ARCH" != "arm64" ] && [ "$ARCH" != "amd64" ] && [ "$ARCH" != "x86_64" ]; then \ echo "Unsupported architecture: $ARCH. Must be 'aarch64', 'arm64', 'amd64', or 'x86_64'." && exit 1; \ @@ -26,9 +31,8 @@ RUN ARCH=$(uname -m) && \ rustup component add rust-src && \ rustup target add $RUST_TARGET && \ RUST_LIBDIR=$(rustc --print target-libdir --target=$RUST_TARGET) && \ - ln -s $RUST_LIBDIR /usr/local/lib/rust && \ - echo "export LD_LIBRARY_PATH=\"${LD_LIBRARY_PATH}:/usr/local/lib/rust\"" >> /etc/bash.bashrc && \ - echo "export RUST_TARGET=\"${RUST_TARGET}\"" >> /etc/bash.bashrc + ln -s $RUST_LIBDIR /usr/local/lib/rust +ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/lib/rust" RUN ARCH=$(uname -m) && \ LLVM_URL=$(if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then \ @@ -42,25 +46,20 @@ RUN ARCH=$(uname -m) && \ rm -rf llvm.tar.zst && \ ln -s /opt/llvm/bin/* /usr/local/bin/ +COPY scripts/ /usr/local/bin/tig-scripts/ +COPY tig-binary/scripts/ /usr/local/bin/tig-scripts/ +RUN chmod +x /usr/local/bin/tig-scripts/* +ENV PATH="/usr/local/bin/tig-scripts:${PATH}" + COPY . /tmp/tig-monorepo WORKDIR /tmp/tig-monorepo -RUN if command -v nvcc > /dev/null 2>&1; then \ - cargo build -r -p tig-runtime --features cuda && \ - cargo build -r -p tig-verifier --features cuda; \ - else \ - cargo build -r -p tig-runtime && \ - cargo build -r -p tig-verifier; \ - fi && \ +RUN cargo build -r -p tig-runtime --features $CHALLENGE && \ + cargo build -r -p tig-verifier --features $CHALLENGE && \ mv target/release/tig-runtime /usr/local/bin/ && \ mv target/release/tig-verifier /usr/local/bin/ && \ chmod +x /usr/local/bin/tig-runtime && \ chmod +x /usr/local/bin/tig-verifier && \ rm -rf tig-monorepo -COPY scripts/ /usr/local/bin/tig-scripts/ -COPY tig-binary/scripts/ /usr/local/bin/tig-scripts/ -RUN chmod +x /usr/local/bin/tig-scripts/* -ENV PATH="/usr/local/bin/tig-scripts:${PATH}" - WORKDIR /app diff --git a/Dockerfile.runtime b/Dockerfile.runtime index 3c84a2e..a291d9d 100644 --- a/Dockerfile.runtime +++ b/Dockerfile.runtime @@ -1,27 +1,28 @@ ARG BASE_IMAGE=ubuntu:24.04 -ARG DEV_IMAGE=ghcr.io/tig-foundation/tig-monorepo/dev +ARG CHALLENGE -FROM ${DEV_IMAGE} AS dev +FROM ghcr.io/tig-foundation/tig-monorepo/${CHALLENGE}/dev AS dev FROM ${BASE_IMAGE} +ARG CHALLENGE +ENV CHALLENGE=${CHALLENGE} +RUN if [ -z "$CHALLENGE" ]; then echo "Error: '--build-arg CHALLENGE' must be set." && exit 1; fi ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && apt install -y python3 python3-pip +RUN pip3 install requests --break-system-packages + +COPY scripts/ /usr/local/bin/tig-scripts/ +RUN chmod +x /usr/local/bin/tig-scripts/* +ENV PATH="/usr/local/bin/tig-scripts:${PATH}" + COPY --from=dev /usr/local/bin/tig-runtime /usr/local/bin/tig-runtime COPY --from=dev /usr/local/bin/tig-verifier /usr/local/bin/tig-verifier COPY --from=dev /usr/local/lib/rust /usr/local/lib/rust RUN chmod +x /usr/local/bin/tig-runtime && \ - chmod +x /usr/local/bin/tig-verifier && \ - echo "export LD_LIBRARY_PATH=\"${LD_LIBRARY_PATH}:/usr/local/lib/rust\"" >> /etc/bash.bashrc - -RUN apt update && apt install -y python3 python3-pip -RUN pip3 install blake3 requests randomname --break-system-packages - -COPY scripts/ /usr/local/bin/tig-scripts/ -RUN chmod +x /usr/local/bin/tig-scripts/* -ENV PATH="/usr/local/bin/tig-scripts:${PATH}" - -COPY tig-benchmarker /app + chmod +x /usr/local/bin/tig-verifier +ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/lib/rust" WORKDIR /app diff --git a/scripts/download_algorithm b/scripts/download_algorithm index 3364665..018ac2b 100644 --- a/scripts/download_algorithm +++ b/scripts/download_algorithm @@ -7,9 +7,13 @@ import requests import sys import tarfile +CHALLENGE = os.getenv("CHALLENGE") +if CHALLENGE is None: + print("CHALLENGE environment variable must be set!") + sys.exit(1) + def main(): parser = argparse.ArgumentParser(description='TIG Algorithm Downloader') - parser.add_argument('challenge', help="Challenge name or id") parser.add_argument('algorithm', help="Algorithm name or id") parser.add_argument('--out', default="tig-algorithms/lib", help="Output directory (default: tig-algorithms/lib)") parser.add_argument('--testnet', action='store_true', help="Use testnet API") @@ -26,12 +30,12 @@ def main(): c_id = next( ( c['id'] for c in challenges - if c['details']['name'] == args.challenge or c['id'] == args.challenge + if c['details']['name'] == CHALLENGE ), None ) if c_id is None: - print(f"Challenge '{args.challenge}' not found.") + print(f"Challenge '{CHALLENGE}' not found.") sys.exit(1) a_id = next( @@ -44,7 +48,7 @@ def main(): None ) if a_id is None: - print(f"Algorithm '{args.algorithm}' not found for challenge '{args.challenge}'.") + print(f"Algorithm '{args.algorithm}' not found for challenge '{CHALLENGE}'.") sys.exit(1) download_url = download_urls.get(a_id) @@ -54,7 +58,7 @@ def main(): print(f"Downloading algorithm '{args.algorithm}' from {download_url}...") resp = requests.get(download_url, stream=True) - output_dir = f"{args.out}/{args.challenge}" + output_dir = f"{args.out}/{CHALLENGE}" os.makedirs(output_dir, exist_ok=True) with tarfile.open(fileobj=io.BytesIO(resp.content), mode='r:gz') as tar: tar.extractall(path=output_dir) diff --git a/scripts/list_algorithms b/scripts/list_algorithms index aa5853c..939237a 100644 --- a/scripts/list_algorithms +++ b/scripts/list_algorithms @@ -1,15 +1,17 @@ #!/usr/bin/env python3 import argparse -import io import os import requests import sys -import tarfile + +CHALLENGE = os.getenv("CHALLENGE") +if CHALLENGE is None: + print("CHALLENGE environment variable must be set!") + sys.exit(1) def main(): parser = argparse.ArgumentParser(description='TIG Algorithm Lister') - parser.add_argument('challenge', help="Challenge name or id") parser.add_argument('--testnet', action='store_true', help="Use testnet API") args = parser.parse_args() @@ -24,12 +26,12 @@ def main(): c_id = next( ( c['id'] for c in challenges - if c['details']['name'] == args.challenge or c['id'] == args.challenge + if c['details']['name'] == CHALLENGE ), None ) if c_id is None: - print(f"Challenge '{args.challenge}' not found.") + print(f"Challenge '{CHALLENGE}' not found.") sys.exit(1) algorithms = sorted([ diff --git a/scripts/test_algorithm b/scripts/test_algorithm index d0e4776..2231d98 100644 --- a/scripts/test_algorithm +++ b/scripts/test_algorithm @@ -37,6 +37,11 @@ elif (VISIBLE_GPUS := os.environ.get("CUDA_VISIBLE_DEVICES", None)) is None: else: VISIBLE_GPUS = list(map(int, VISIBLE_GPUS.split(","))) +CHALLENGE = os.getenv("CHALLENGE") +if CHALLENGE is None: + print("CHALLENGE environment variable must be set!") + sys.exit(1) + def now(): return int(time.time() * 1000) @@ -44,7 +49,6 @@ def now(): if __name__ == "__main__": tig_runtime_path = shutil.which("tig-runtime") parser = argparse.ArgumentParser(description="TIG Algorithm Tester") - parser.add_argument("challenge", type=str, help="Challenge name") parser.add_argument("algorithm", type=str, help="Algorithm name") parser.add_argument("difficulty", type=str, help="JSON string of difficulty") parser.add_argument("--tig_runtime_path", type=str, default=tig_runtime_path, help=f"Path to tig-runtime executable (default: {tig_runtime_path})") @@ -58,8 +62,8 @@ if __name__ == "__main__": args = parser.parse_args() - so_path = f"{args.lib_dir}/{args.challenge}/{CPU_ARCH}/{args.algorithm}.so" - ptx_path = f"{args.lib_dir}/{args.challenge}/ptx/{args.algorithm}.ptx" + so_path = f"{args.lib_dir}/{CHALLENGE}/{CPU_ARCH}/{args.algorithm}.so" + ptx_path = f"{args.lib_dir}/{CHALLENGE}/ptx/{args.algorithm}.ptx" if not os.path.exists(so_path): print( @@ -93,10 +97,7 @@ f"""Library not found at {so_path}: "hypergraph": "c005", "optimiser": "c006", } - challenge_id = challenge_ids.get(args.challenge, None) - if challenge_id is None: - print(f"Unknown challenge '{args.challenge}'. Choices: ({', '.join(challenge_ids.keys())})") - sys.exit(1) + challenge_id = challenge_ids[CHALLENGE] settings = {"algorithm_id": "", "challenge_id": challenge_id, "difficulty": difficulty, "block_id": "", "player_id": ""} pool = ThreadPoolExecutor(max_workers=args.workers + 1) diff --git a/tig-algorithms/Cargo.toml b/tig-algorithms/Cargo.toml index b76f412..f8d1d22 100644 --- a/tig-algorithms/Cargo.toml +++ b/tig-algorithms/Cargo.toml @@ -23,4 +23,14 @@ tig-challenges = { path = "../tig-challenges" } crate-type = ["cdylib", "rlib"] [features] -cuda = ["cudarc", "tig-challenges/cuda"] +cuda = ["cudarc"] +c001 = ["tig-challenges/c001"] +satisfiability = ["c001"] +c002 = ["tig-challenges/c002"] +vehicle_routing = ["c002"] +c003 = ["tig-challenges/c003"] +knapsack = ["c003"] +c004 = ["cuda", "tig-challenges/c004"] +vector_search = ["c004"] +c005 = ["cuda", "tig-challenges/c005"] +hypergraph = ["c005"] diff --git a/tig-algorithms/src/lib.rs b/tig-algorithms/src/lib.rs index 3f7af0f..67a1f54 100644 --- a/tig-algorithms/src/lib.rs +++ b/tig-algorithms/src/lib.rs @@ -11,18 +11,23 @@ pub(crate) type HashSet = std::collections::HashSet; pub const BUILD_TIME_PATH: &str = env!("CARGO_MANIFEST_DIR"); -pub mod knapsack; -pub use knapsack as c003; +#[cfg(feature = "c001")] pub mod satisfiability; +#[cfg(feature = "c001")] pub use satisfiability as c001; +#[cfg(feature = "c002")] pub mod vehicle_routing; +#[cfg(feature = "c002")] pub use vehicle_routing as c002; - -#[cfg(feature = "cuda")] +#[cfg(feature = "c003")] +pub mod knapsack; +#[cfg(feature = "c003")] +pub use knapsack as c003; +#[cfg(feature = "c004")] pub mod vector_search; -#[cfg(feature = "cuda")] +#[cfg(feature = "c004")] pub use vector_search as c004; -#[cfg(feature = "cuda")] +#[cfg(feature = "c005")] pub mod hypergraph; -#[cfg(feature = "cuda")] +#[cfg(feature = "c005")] pub use hypergraph as c005; diff --git a/tig-binary/Cargo.toml b/tig-binary/Cargo.toml index 0133509..008ebbc 100644 --- a/tig-binary/Cargo.toml +++ b/tig-binary/Cargo.toml @@ -19,4 +19,14 @@ tig-algorithms = { path = "../tig-algorithms" } [features] entry_point = [] -cuda = ["cudarc", "tig-algorithms/cuda", "tig-challenges/cuda"] +cuda = ["cudarc"] +c001 = ["tig-algorithms/c001", "tig-challenges/c001"] +satisfiability = ["c001"] +c002 = ["tig-algorithms/c002", "tig-challenges/c002"] +vehicle_routing = ["c002"] +c003 = ["tig-algorithms/c003", "tig-challenges/c003"] +knapsack = ["c003"] +c004 = ["cuda", "tig-algorithms/c004", "tig-challenges/c004"] +vector_search = ["c004"] +c005 = ["cuda", "tig-algorithms/c005", "tig-challenges/c005"] +hypergraph = ["c005"] diff --git a/tig-binary/scripts/build_algorithm b/tig-binary/scripts/build_algorithm new file mode 100644 index 0000000..cb6323f --- /dev/null +++ b/tig-binary/scripts/build_algorithm @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e + +# Check if CHALLENGE environment variable is set +if [ -z "$CHALLENGE" ]; then + echo "Error: CHALLENGE environment variable is not set." + exit 1 +fi + +# Check if ALGORITHM argument is provided +if [ -z "$1" ]; then + echo "Error: ALGORITHM argument is required." + echo "Usage: $0 " + exit 1 +fi + +ALGORITHM="$1" + +# Match CHALLENGE value +case "$CHALLENGE" in + satisfiability) + echo "Building ALGORITHM '$ALGORITHM' for CHALLENGE 'satisfiability'" + build_so $ALGORITHM + ;; + vehicle_routing) + echo "Building ALGORITHM '$ALGORITHM' for CHALLENGE 'vehicle_routing'" + build_so $ALGORITHM + ;; + knapsack) + echo "Building ALGORITHM '$ALGORITHM' for CHALLENGE 'knapsack'" + build_so $ALGORITHM + ;; + vector_search) + echo "Building ALGORITHM '$ALGORITHM' for CHALLENGE 'vector_search'" + build_so $ALGORITHM + build_ptx $ALGORITHM + ;; + hypergraph) + echo "Building ALGORITHM '$ALGORITHM' for CHALLENGE 'hypergraph'" + build_so $ALGORITHM + build_ptx $ALGORITHM + ;; + *) + echo "Error: Invalid CHALLENGE value. Must be one of: satisfiability, knapsack, vehicle_routing, vector_search, hypergraph" + exit 1 + ;; +esac \ No newline at end of file diff --git a/tig-binary/scripts/build_ptx b/tig-binary/scripts/build_ptx index fff9099..ed88b6a 100644 --- a/tig-binary/scripts/build_ptx +++ b/tig-binary/scripts/build_ptx @@ -3,11 +3,15 @@ import argparse import os import re -import shutil import subprocess import sys import tempfile +CHALLENGE = os.getenv("CHALLENGE") +if CHALLENGE is None: + print("CHALLENGE environment variable must be set!") + sys.exit(1) + # Import the dictionary from ptx_instructions.py instruction_fuel_cost = { 'add.u32': 2, @@ -232,12 +236,11 @@ $NORMAL_EXIT: def main(): parser = argparse.ArgumentParser(description='Compile PTX with injected runtime signature') - parser.add_argument('challenge', help='Challenge name') parser.add_argument('algorithm', help='Algorithm name') args = parser.parse_args() - print(f"Compiling .ptx for {args.challenge}/{args.algorithm}") + print(f"Compiling .ptx for {CHALLENGE}/{args.algorithm}") framework_cu = "tig-binary/src/framework.cu" if not os.path.exists(framework_cu): @@ -245,14 +248,14 @@ def main(): f"Framework code does not exist @ '{framework_cu}'. This script must be run from the root of tig-monorepo" ) - challenge_cu = f"tig-challenges/src/{args.challenge}.cu" + challenge_cu = f"tig-challenges/src/{CHALLENGE}.cu" if not os.path.exists(challenge_cu): raise FileNotFoundError( f"Challenge code does not exist @ '{challenge_cu}'. Is the challenge name correct?" ) - algorithm_cu = f"tig-algorithms/src/{args.challenge}/{args.algorithm}.cu" - algorithm_cu2 = f"tig-algorithms/src/{args.challenge}/{args.algorithm}/benchmarker_outbound.cu" + algorithm_cu = f"tig-algorithms/src/{CHALLENGE}/{args.algorithm}.cu" + algorithm_cu2 = f"tig-algorithms/src/{CHALLENGE}/{args.algorithm}/benchmarker_outbound.cu" if not os.path.exists(algorithm_cu) and not os.path.exists(algorithm_cu2): raise FileNotFoundError( f"Algorithm code does not exist @ '{algorithm_cu}' or '{algorithm_cu2}'. Is the algorithm name correct?" @@ -295,10 +298,8 @@ def main(): parsed = parse_ptx_code(ptx_code) modified_code = inject_fuel_and_runtime_sig(parsed, kernels_to_ignore) - output_path = f"tig-algorithms/lib/{args.challenge}/ptx/{args.algorithm}.ptx" + output_path = f"tig-algorithms/lib/{CHALLENGE}/ptx/{args.algorithm}.ptx" os.makedirs(os.path.dirname(output_path), exist_ok=True) - # with open(output_path, 'w') as f: - # f.writelines(ptx_code) with open(output_path, 'w') as f: f.writelines(modified_code) print(f"Wrote ptx to {output_path}") diff --git a/tig-binary/scripts/build_so b/tig-binary/scripts/build_so index 0f2ec57..be7beb4 100644 --- a/tig-binary/scripts/build_so +++ b/tig-binary/scripts/build_so @@ -2,55 +2,34 @@ set -e -CUDA=false - -if [ $# -lt 2 ]; then - echo "Error: Missing required arguments" - echo "Usage: $0 CHALLENGE ALGORITHM [--cuda]" - echo "Example: $0 knapsack dynamic" - echo "Example with CUDA: $0 knapsack dynamic --cuda" - exit 1 -fi - -CHALLENGE="$1" -ALGORITHM="$2" - -shift 2 -while [ $# -gt 0 ]; do - case "$1" in - --cuda) - CUDA=true - ;; - *) - echo "Unknown option: $1" - echo "Usage: $0 CHALLENGE ALGORITHM [--cuda]" - exit 1 - ;; - esac - shift -done - +# Check if CHALLENGE environment variable is set if [ -z "$CHALLENGE" ]; then - echo "Error: CHALLENGE argument is empty" - exit 1 + echo "Error: CHALLENGE environment variable is not set." + exit 1 fi -if [ -z "$ALGORITHM" ]; then - echo "Error: ALGORITHM argument is empty" - exit 1 +# Check if ALGORITHM argument is provided +if [ -z "$1" ]; then + echo "Error: ALGORITHM argument is required." + echo "Usage: $0 " + exit 1 fi +ALGORITHM="$1" + echo "Compiling .so for $CHALLENGE/$ALGORITHM" cp tig-binary/src/entry_point_template.rs tig-binary/src/entry_point.rs sed -i "s/{CHALLENGE}/$CHALLENGE/g" tig-binary/src/entry_point.rs sed -i "s/{ALGORITHM}/$ALGORITHM/g" tig-binary/src/entry_point.rs -if [ "$CUDA" = true ]; then - FEATURES="entry_point cuda" -else - FEATURES="entry_point" -fi +FEATURES="entry_point $CHALLENGE" +ARCH=$(uname -m) +RUST_TARGET=$(if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then + echo "aarch64-unknown-linux-gnu"; +else + echo "x86_64-unknown-linux-gnu"; +fi) RUSTFLAGS="--emit=llvm-ir -C embed-bitcode=yes -C codegen-units=1 -C opt-level=3 -C lto=no -C debuginfo=2 -C relocation-model=pic" \ cargo +nightly-2025-02-10 build \ -p tig-binary \ diff --git a/tig-challenges/Cargo.toml b/tig-challenges/Cargo.toml index 2e68894..7f42495 100644 --- a/tig-challenges/Cargo.toml +++ b/tig-challenges/Cargo.toml @@ -23,3 +23,13 @@ statrs = { version = "0.18.0" } [features] cuda = ["cudarc"] +c001 = [] +satisfiability = ["c001"] +c002 = [] +vehicle_routing = ["c002"] +c003 = [] +knapsack = ["c003"] +c004 = ["cuda"] +vector_search = ["c004"] +c005 = ["cuda"] +hypergraph = ["c005"] diff --git a/tig-challenges/src/lib.rs b/tig-challenges/src/lib.rs index cf41e17..c900e80 100644 --- a/tig-challenges/src/lib.rs +++ b/tig-challenges/src/lib.rs @@ -1,17 +1,22 @@ pub const BUILD_TIME_PATH: &str = env!("CARGO_MANIFEST_DIR"); -pub mod knapsack; -pub use knapsack as c003; +#[cfg(feature = "c001")] pub mod satisfiability; +#[cfg(feature = "c001")] pub use satisfiability as c001; +#[cfg(feature = "c002")] pub mod vehicle_routing; +#[cfg(feature = "c002")] pub use vehicle_routing as c002; - -#[cfg(feature = "cuda")] +#[cfg(feature = "c003")] +pub mod knapsack; +#[cfg(feature = "c003")] +pub use knapsack as c003; +#[cfg(feature = "c004")] pub mod vector_search; -#[cfg(feature = "cuda")] +#[cfg(feature = "c004")] pub use vector_search as c004; -#[cfg(feature = "cuda")] +#[cfg(feature = "c005")] pub mod hypergraph; -#[cfg(feature = "cuda")] +#[cfg(feature = "c005")] pub use hypergraph as c005; diff --git a/tig-runtime/Cargo.toml b/tig-runtime/Cargo.toml index 2383ce5..3802846 100644 --- a/tig-runtime/Cargo.toml +++ b/tig-runtime/Cargo.toml @@ -21,4 +21,14 @@ tig-structs = { path = "../tig-structs" } tig-utils = { path = "../tig-utils" } [features] -cuda = ["cudarc", "tig-challenges/cuda"] +cuda = ["cudarc"] +c001 = ["tig-challenges/c001"] +satisfiability = ["c001"] +c002 = ["tig-challenges/c002"] +vehicle_routing = ["c002"] +c003 = ["tig-challenges/c003"] +knapsack = ["c003"] +c004 = ["cuda", "tig-challenges/c004"] +vector_search = ["c004"] +c005 = ["cuda", "tig-challenges/c005"] +hypergraph = ["c005"] diff --git a/tig-runtime/src/main.rs b/tig-runtime/src/main.rs index bf3db5a..757d855 100644 --- a/tig-runtime/src/main.rs +++ b/tig-runtime/src/main.rs @@ -51,7 +51,6 @@ fn cli() -> Command { ) .arg( arg!(--gpu [GPU] "Which GPU device to use") - .default_value("0") .value_parser(clap::value_parser!(usize)), ) } @@ -68,7 +67,7 @@ fn main() { *matches.get_one::("fuel").unwrap(), matches.get_one::("output").cloned(), matches.get_one::("compress").unwrap().clone(), - matches.get_one::("gpu").unwrap().clone(), + matches.get_one::("gpu").cloned(), ) { eprintln!("Error: {}", e); std::process::exit(1); @@ -80,13 +79,11 @@ pub fn compute_solution( rand_hash: String, nonce: u64, library_path: PathBuf, - #[cfg(feature = "cuda")] ptx_path: Option, - #[cfg(not(feature = "cuda"))] _ptx_path: Option, + ptx_path: Option, max_fuel: u64, output_file: Option, compress: bool, - #[cfg(feature = "cuda")] gpu_device: usize, - #[cfg(not(feature = "cuda"))] _gpu_device: usize, + gpu_device: Option, ) -> Result<()> { let settings = load_settings(&settings); let seed = settings.calc_seed(&rand_hash, nonce); @@ -97,7 +94,12 @@ pub fn compute_solution( let runtime_signature_ptr = unsafe { *library.get::<*mut u64>(b"__runtime_signature")? }; unsafe { *runtime_signature_ptr = u64::from_be_bytes(seed[0..8].try_into().unwrap()) }; - let (fuel_consumed, runtime_signature, solution, invalid_reason) = 'out_of_fuel: { + let (fuel_consumed, runtime_signature, solution, invalid_reason): ( + u64, + u64, + Solution, + Option, + ) = 'out_of_fuel: { macro_rules! dispatch_challenge { ($c:ident, cpu) => {{ // library function may exit 87 if it runs out of fuel @@ -138,141 +140,157 @@ pub fn compute_solution( }}; ($c:ident, gpu) => {{ - #[cfg(not(feature = "cuda"))] - panic!("tig-runtime was not compiled with '--features cuda'"); - - #[cfg(feature = "cuda")] - { - if ptx_path.is_none() { - panic!("PTX file is required for GPU challenges."); - } - let ptx_path = ptx_path.unwrap(); - // library function may exit 87 if it runs out of fuel - let solve_challenge_fn = unsafe { - library.get::, - Arc, - &cudaDeviceProp, - ) -> Result, String>>( - b"entry_point" - )? - }; - - let gpu_fuel_scale = 20; // scale fuel to loosely align with CPU - let ptx_content = std::fs::read_to_string(&ptx_path) - .map_err(|e| anyhow!("Failed to read PTX file: {}", e))?; - let max_fuel_hex = format!("0x{:016x}", max_fuel * gpu_fuel_scale); - let modified_ptx = ptx_content.replace("0xdeadbeefdeadbeef", &max_fuel_hex); - - let ptx = Ptx::from_src(modified_ptx); - let ctx = CudaContext::new(gpu_device)?; - ctx.set_blocking_synchronize()?; - let module = ctx.load_module(ptx)?; - let stream = ctx.fuel_check_stream(); - let prop = get_device_prop(gpu_device as i32)?; - - let challenge = $c::Challenge::generate_instance( - &seed, - &settings.difficulty.into(), - module.clone(), - stream.clone(), - &prop, - )?; - - let initialize_kernel = module.load_function("initialize_kernel")?; - - let cfg = LaunchConfig { - grid_dim: (1, 1, 1), - block_dim: (1, 1, 1), - shared_mem_bytes: 0, - }; - - unsafe { - stream - .launch_builder(&initialize_kernel) - .arg(&(u64::from_be_bytes(seed[8..16].try_into().unwrap()))) - .launch(cfg)?; - } - - let result = - solve_challenge_fn(&challenge, module.clone(), stream.clone(), &prop); - if result - .as_ref() - .is_err_and(|e| e.contains("ran out of fuel")) - { - break 'out_of_fuel (max_fuel + 1, 0, Solution::new(), None); - } - let result = result.map_err(|e| anyhow!("{}", e))?; - stream.synchronize()?; - ctx.synchronize()?; - - let mut fuel_usage = stream.alloc_zeros::(1)?; - let mut signature = stream.alloc_zeros::(1)?; - let mut error_stat = stream.alloc_zeros::(1)?; - - let finalize_kernel = module.load_function("finalize_kernel")?; - - let cfg = LaunchConfig { - grid_dim: (1, 1, 1), - block_dim: (1, 1, 1), - shared_mem_bytes: 0, - }; - - unsafe { - stream - .launch_builder(&finalize_kernel) - .arg(&mut fuel_usage) - .arg(&mut signature) - .arg(&mut error_stat) - .launch(cfg)?; - } - - if stream.memcpy_dtov(&error_stat)?[0] != 0 { - break 'out_of_fuel (max_fuel + 1, 0, Solution::new(), None); - } - let fuel_consumed = (stream.memcpy_dtov(&fuel_usage)?[0] / gpu_fuel_scale - + max_fuel - - unsafe { **library.get::<*const u64>(b"__fuel_remaining")? }); - - if fuel_consumed > max_fuel { - break 'out_of_fuel (max_fuel + 1, 0, Solution::new(), None); - } - - let runtime_signature = (stream.memcpy_dtov(&signature)?[0] - ^ unsafe { **library.get::<*const u64>(b"__runtime_signature")? }); - - let (solution, invalid_reason) = match result { - Some(s) => ( - serde_json::to_value(&s) - .unwrap() - .as_object() - .unwrap() - .to_owned(), - match challenge.verify_solution( - &s, - module.clone(), - stream.clone(), - &prop, - ) { - Ok(_) => None, - Err(e) => Some(e.to_string()), - }, - ), - None => (Solution::new(), None), - }; - - (fuel_consumed, runtime_signature, solution, invalid_reason) + if ptx_path.is_none() { + panic!("PTX file is required for GPU challenges."); } + let ptx_path = ptx_path.unwrap(); + // library function may exit 87 if it runs out of fuel + let solve_challenge_fn = unsafe { + library.get::, + Arc, + &cudaDeviceProp, + ) -> Result, String>>(b"entry_point")? + }; + + let gpu_fuel_scale = 20; // scale fuel to loosely align with CPU + let ptx_content = std::fs::read_to_string(&ptx_path) + .map_err(|e| anyhow!("Failed to read PTX file: {}", e))?; + let max_fuel_hex = format!("0x{:016x}", max_fuel * gpu_fuel_scale); + let modified_ptx = ptx_content.replace("0xdeadbeefdeadbeef", &max_fuel_hex); + + let num_gpus = CudaContext::device_count()?; + if num_gpus == 0 { + panic!("No CUDA devices found"); + } + let gpu_device = gpu_device.unwrap_or((nonce % num_gpus as u64) as usize); + let ptx = Ptx::from_src(modified_ptx); + let ctx = CudaContext::new(gpu_device)?; + ctx.set_blocking_synchronize()?; + let module = ctx.load_module(ptx)?; + let stream = ctx.fuel_check_stream(); + let prop = get_device_prop(gpu_device as i32)?; + + let challenge = $c::Challenge::generate_instance( + &seed, + &settings.difficulty.into(), + module.clone(), + stream.clone(), + &prop, + )?; + + let initialize_kernel = module.load_function("initialize_kernel")?; + + let cfg = LaunchConfig { + grid_dim: (1, 1, 1), + block_dim: (1, 1, 1), + shared_mem_bytes: 0, + }; + + unsafe { + stream + .launch_builder(&initialize_kernel) + .arg(&(u64::from_be_bytes(seed[8..16].try_into().unwrap()))) + .launch(cfg)?; + } + + let result = solve_challenge_fn(&challenge, module.clone(), stream.clone(), &prop); + if result + .as_ref() + .is_err_and(|e| e.contains("ran out of fuel")) + { + break 'out_of_fuel (max_fuel + 1, 0, Solution::new(), None); + } + let result = result.map_err(|e| anyhow!("{}", e))?; + stream.synchronize()?; + ctx.synchronize()?; + + let mut fuel_usage = stream.alloc_zeros::(1)?; + let mut signature = stream.alloc_zeros::(1)?; + let mut error_stat = stream.alloc_zeros::(1)?; + + let finalize_kernel = module.load_function("finalize_kernel")?; + + let cfg = LaunchConfig { + grid_dim: (1, 1, 1), + block_dim: (1, 1, 1), + shared_mem_bytes: 0, + }; + + unsafe { + stream + .launch_builder(&finalize_kernel) + .arg(&mut fuel_usage) + .arg(&mut signature) + .arg(&mut error_stat) + .launch(cfg)?; + } + + if stream.memcpy_dtov(&error_stat)?[0] != 0 { + break 'out_of_fuel (max_fuel + 1, 0, Solution::new(), None); + } + let fuel_consumed = (stream.memcpy_dtov(&fuel_usage)?[0] / gpu_fuel_scale + + max_fuel + - unsafe { **library.get::<*const u64>(b"__fuel_remaining")? }); + + if fuel_consumed > max_fuel { + break 'out_of_fuel (max_fuel + 1, 0, Solution::new(), None); + } + + let runtime_signature = (stream.memcpy_dtov(&signature)?[0] + ^ unsafe { **library.get::<*const u64>(b"__runtime_signature")? }); + + let (solution, invalid_reason) = match result { + Some(s) => ( + serde_json::to_value(&s) + .unwrap() + .as_object() + .unwrap() + .to_owned(), + match challenge.verify_solution(&s, module.clone(), stream.clone(), &prop) { + Ok(_) => None, + Err(e) => Some(e.to_string()), + }, + ), + None => (Solution::new(), None), + }; + + (fuel_consumed, runtime_signature, solution, invalid_reason) }}; } match settings.challenge_id.as_str() { - "c001" => dispatch_challenge!(c001, cpu), - "c002" => dispatch_challenge!(c002, cpu), - "c003" => dispatch_challenge!(c003, cpu), - "c004" => dispatch_challenge!(c004, gpu), - "c005" => dispatch_challenge!(c005, gpu), + "c001" => { + #[cfg(not(feature = "c001"))] + panic!("tig-runtime was not compiled with '--features c001'"); + #[cfg(feature = "c001")] + dispatch_challenge!(c001, cpu) + } + "c002" => { + #[cfg(not(feature = "c002"))] + panic!("tig-runtime was not compiled with '--features c002'"); + #[cfg(feature = "c002")] + dispatch_challenge!(c002, cpu) + } + "c003" => { + #[cfg(not(feature = "c003"))] + panic!("tig-runtime was not compiled with '--features c003'"); + #[cfg(feature = "c003")] + dispatch_challenge!(c003, cpu) + } + "c004" => { + #[cfg(not(feature = "c004"))] + panic!("tig-runtime was not compiled with '--features c004'"); + #[cfg(feature = "c004")] + dispatch_challenge!(c004, gpu) + } + "c005" => { + #[cfg(not(feature = "c005"))] + panic!("tig-runtime was not compiled with '--features c005'"); + #[cfg(feature = "c005")] + dispatch_challenge!(c005, gpu) + } _ => panic!("Unsupported challenge"), } }; diff --git a/tig-verifier/Cargo.toml b/tig-verifier/Cargo.toml index 19104ce..c2f19c3 100644 --- a/tig-verifier/Cargo.toml +++ b/tig-verifier/Cargo.toml @@ -19,4 +19,14 @@ tig-structs = { path = "../tig-structs" } tig-utils = { path = "../tig-utils" } [features] -cuda = ["cudarc", "tig-challenges/cuda"] +cuda = ["cudarc"] +c001 = ["tig-challenges/c001"] +satisfiability = ["c001"] +c002 = ["tig-challenges/c002"] +vehicle_routing = ["c002"] +c003 = ["tig-challenges/c003"] +knapsack = ["c003"] +c004 = ["cuda", "tig-challenges/c004"] +vector_search = ["c004"] +c005 = ["cuda", "tig-challenges/c005"] +hypergraph = ["c005"] diff --git a/tig-verifier/src/main.rs b/tig-verifier/src/main.rs index e05eb5b..3ed9154 100644 --- a/tig-verifier/src/main.rs +++ b/tig-verifier/src/main.rs @@ -34,7 +34,6 @@ fn cli() -> Command { ) .arg( arg!(--gpu [GPU] "Which GPU device to use") - .default_value("0") .value_parser(clap::value_parser!(usize)), ), ) @@ -59,7 +58,7 @@ fn main() { *sub_m.get_one::("NONCE").unwrap(), sub_m.get_one::("SOLUTION").unwrap().clone(), sub_m.get_one::("ptx").cloned(), - sub_m.get_one::("gpu").unwrap().clone(), + sub_m.get_one::("gpu").cloned(), ), Some(("verify_merkle_proof", sub_m)) => verify_merkle_proof( sub_m.get_one::("ROOT").unwrap().clone(), @@ -78,7 +77,7 @@ pub fn verify_solution( nonce: u64, solution_path: String, ptx_path: Option, - gpu_device: usize, + gpu_device: Option, ) -> Result<()> { let settings = load_settings(&settings); let solution = load_solution(&solution_path); @@ -86,90 +85,110 @@ pub fn verify_solution( let mut err_msg = Option::::None; - macro_rules! dispatch_challenges { - ( $( ($c:ident, $cpu_or_gpu:tt) ),+ $(,)? ) => {{ - match settings.challenge_id.as_str() { - $( - stringify!($c) => { - dispatch_challenges!(@expand $c, $cpu_or_gpu); - } - )+ - _ => panic!("Unsupported challenge"), + macro_rules! dispatch_challenge { + ($c:ident, cpu) => {{ + let challenge = + $c::Challenge::generate_instance(&seed, &settings.difficulty.into()).unwrap(); + + match $c::Solution::try_from(solution) { + Ok(solution) => match challenge.verify_solution(&solution) { + Ok(_) => println!("Solution is valid"), + Err(e) => err_msg = Some(format!("Invalid solution: {}", e)), + }, + Err(_) => { + err_msg = Some(format!( + "Invalid solution. Cannot convert to {}::Solution", + stringify!($c) + )) + } } }}; - (@expand $c:ident, cpu) => {{ + ($c:ident, gpu) => {{ + if ptx_path.is_none() { + panic!("PTX file is required for GPU challenges."); + } + + let num_gpus = CudaContext::device_count()?; + if num_gpus == 0 { + panic!("No CUDA devices found"); + } + let gpu_device = gpu_device.unwrap_or((nonce % num_gpus as u64) as usize); + let ptx = Ptx::from_file(ptx_path.unwrap()); + let ctx = CudaContext::new(gpu_device).unwrap(); + ctx.set_blocking_synchronize()?; + let module = ctx.load_module(ptx).unwrap(); + let stream = ctx.default_stream(); + let prop = get_device_prop(gpu_device as i32).unwrap(); + let challenge = $c::Challenge::generate_instance( &seed, &settings.difficulty.into(), - ).unwrap(); + module.clone(), + stream.clone(), + &prop, + ) + .unwrap(); match $c::Solution::try_from(solution) { Ok(solution) => { - match challenge.verify_solution(&solution) { - Ok(_) => println!("Solution is valid"), + match challenge.verify_solution( + &solution, + module.clone(), + stream.clone(), + &prop, + ) { + Ok(_) => { + stream.synchronize()?; + ctx.synchronize()?; + println!("Solution is valid"); + } Err(e) => err_msg = Some(format!("Invalid solution: {}", e)), } - }, - Err(_) => err_msg = Some(format!( - "Invalid solution. Cannot convert to {}::Solution", - stringify!($c) - )), - } - }}; - - (@expand $c:ident, gpu) => {{ - #[cfg(not(feature = "cuda"))] - panic!("tig-runtime was not compiled with '--features cuda'"); - - #[cfg(feature = "cuda")] - { - if ptx_path.is_none() { - panic!("PTX file is required for GPU challenges."); } - - let ptx = Ptx::from_file(ptx_path.unwrap()); - let ctx = CudaContext::new(gpu_device).unwrap(); - ctx.set_blocking_synchronize()?; - let module = ctx.load_module(ptx).unwrap(); - let stream = ctx.default_stream(); - let prop = get_device_prop(gpu_device as i32).unwrap(); - - let challenge = $c::Challenge::generate_instance( - &seed, - &settings.difficulty.into(), - module.clone(), - stream.clone(), - &prop, - ).unwrap(); - - match $c::Solution::try_from(solution) { - Ok(solution) => { - match challenge.verify_solution(&solution, module.clone(), stream.clone(), &prop) { - Ok(_) => { - stream.synchronize()?; - ctx.synchronize()?; - println!("Solution is valid"); - }, - Err(e) => err_msg = Some(format!("Invalid solution: {}", e)), - } - }, - Err(_) => err_msg = Some(format!( + Err(_) => { + err_msg = Some(format!( "Invalid solution. Cannot convert to {}::Solution", stringify!($c) - )), + )) } } }}; } - dispatch_challenges!( - (c001, cpu), - (c002, cpu), - (c003, cpu), - (c004, gpu), - (c005, gpu) - ); + match settings.challenge_id.as_str() { + "c001" => { + #[cfg(not(feature = "c001"))] + panic!("tig-verifier was not compiled with '--features c001'"); + #[cfg(feature = "c001")] + dispatch_challenge!(c001, cpu) + } + "c002" => { + #[cfg(not(feature = "c002"))] + panic!("tig-verifier was not compiled with '--features c002'"); + #[cfg(feature = "c002")] + dispatch_challenge!(c002, cpu) + } + "c003" => { + #[cfg(not(feature = "c003"))] + panic!("tig-verifier was not compiled with '--features c003'"); + #[cfg(feature = "c003")] + dispatch_challenge!(c003, cpu) + } + "c004" => { + #[cfg(not(feature = "c004"))] + panic!("tig-verifier was not compiled with '--features c004'"); + #[cfg(feature = "c004")] + dispatch_challenge!(c004, gpu) + } + "c005" => { + #[cfg(not(feature = "c005"))] + panic!("tig-verifier was not compiled with '--features c005'"); + #[cfg(feature = "c005")] + dispatch_challenge!(c005, gpu) + } + _ => panic!("Unsupported challenge"), + } if let Some(err_msg) = err_msg { eprintln!("Verification error: {}", err_msg);