diff --git a/src/compiler/nvcc.rs b/src/compiler/nvcc.rs index 2b43d51234..bfc61dbe12 100644 --- a/src/compiler/nvcc.rs +++ b/src/compiler/nvcc.rs @@ -253,24 +253,6 @@ pub fn generate_compile_commands( cwd: &Path, env_vars: &[(OsString, OsString)], ) -> Result<(NvccCompileCommand, Option, Cacheable)> { - let env_vars = env_vars - .iter() - .filter(|(k, _)| k != "NVCC_PREPEND_FLAGS" && k != "NVCC_APPEND_FLAGS") - .cloned() - .collect::>(); - - let temp_dir = tempfile::Builder::new() - .prefix("sccache_nvcc") - .tempdir() - .unwrap() - .into_path(); - - let mut arguments: Vec = vec![ - "--dryrun".into(), - // Tell nvcc to produce deterministic file names - "--keep".into(), - ]; - let mut unhashed_args = parsed_args.unhashed_args.clone(); let keep_dir = { @@ -340,6 +322,20 @@ pub fn generate_compile_commands( num_parallel }; + let env_vars = env_vars + .iter() + .filter(|(k, _)| k != "NVCC_PREPEND_FLAGS" && k != "NVCC_APPEND_FLAGS") + .cloned() + .collect::>(); + + let temp_dir = tempfile::Builder::new() + .prefix("sccache_nvcc") + .tempdir() + .unwrap() + .into_path(); + + let mut arguments = vec![]; + if let Some(lang) = gcc::language_to_gcc_arg(parsed_args.language) { arguments.extend(vec!["-x".into(), lang.into()]) } @@ -559,213 +555,113 @@ async fn group_nvcc_subcommands_by_compilation_stage( executable: &Path, arguments: &[OsString], cwd: &Path, - temp_dir: &Path, + tmp: &Path, keep_dir: Option, env_vars: &mut Vec<(OsString, OsString)>, ) -> Result>> where T: CommandCreatorSync, { - if log_enabled!(log::Level::Trace) { - trace!( - "nvcc dryrun cmd: {:?}", - [ - vec![executable.as_os_str().to_owned()], - arguments.to_owned() - ] - .concat() - .join(OsStr::new(" ")) - ); - } - - fn select_valid_dryrun_lines(re: &Regex, line: &str) -> Result { - match re.captures(line) { - Some(caps) => { - let (_, [rest]) = caps.extract(); - Ok(rest.to_string()) - } - _ => Err(anyhow!("nvcc error: {:?}", line)), - } - } - - fn fold_env_vars_or_split_into_exe_and_args( - re: &Regex, - env_vars: &mut Vec<(OsString, OsString)>, - cwd: &Path, - line: &str, - ) -> Option<(PathBuf, Vec)> { - // Intercept the environment variable lines and add them to the env_vars list - if let Some(var) = re.captures(line) { - let (_, [var, val]) = var.extract(); - - let loc = if let Some(idx) = env_vars.iter().position(|(key, _)| key == var) { - idx..idx + 1 - } else { - env_vars.len()..env_vars.len() - }; - - let mut pair = (var.into(), val.into()); - // Handle the special `_SPACE_= ` line - if val != " " { - pair.1 = val - .trim() - .split(' ') - .map(|x| x.trim_start_matches('\"').trim_end_matches('\"')) - .collect::>() - .join(" ") - .into(); - } - env_vars.splice(loc, [pair]); - return None; - } - - // The rest of the lines are subcommands, so parse into a vec of [cmd, args..] - - let mut line = line.to_owned(); - - // Expand envvars in nvcc subcommands, i.e. "$CICC_PATH/cicc ..." - if let Some(env_vars) = dist::osstring_tuples_to_strings(env_vars) { - for (key, val) in env_vars { - let var = "$".to_owned() + &key; - line = line.replace(&var, &val); - } - } - - let args = shlex::split(&line)?; - let (exe, args) = args.split_first()?; - - let env_path = env_vars - .iter() - .find(|(k, _)| k == "PATH") - .map(|(_, p)| p.to_owned()) - .unwrap(); - - if let Ok(exe) = which_in(exe, env_path.into(), cwd) { - return Some((exe.clone(), args.to_vec())); - } - - None - } - - fn remap_generated_filenames( - args: &[String], - old_to_new: &mut HashMap, - ext_counts: &mut HashMap, - ) -> Vec { - args.iter() - .map(|arg| { - // If the argument doesn't start with `-` and is a file that - // ends in one of the below extensions, rename the file to an - // auto-incrementing stable name - let maybe_extension = (!arg.starts_with('-')) - .then(|| { - [ - ".cpp1.ii", - ".cpp4.ii", - ".cudafe1.c", - ".cudafe1.cpp", - ".cudafe1.stub.c", - ] - .iter() - .find(|ext| arg.ends_with(*ext)) - .copied() - }) - .unwrap_or(None); - - // If the argument is a file that ends in one of the above extensions: - // * If it's our first time seeing this file, create a unique name for it - // * If we've seen this file before, lookup its unique name in the hash map - // - // This ensures stable names are in cudafe++ output and #include directives, - // eliminating one source of false-positive cache misses. - match maybe_extension { - Some(extension) => { - old_to_new - .entry(arg.into()) - .or_insert_with(|| { - // Initialize or update the number of files with a given extension: - // compute_70.cudafe1.stub.c -> 0.cudafe1.stub.c - // compute_60.cudafe1.stub.c -> 1.cudafe1.stub.c - // etc. - let count = ext_counts - .entry(extension.into()) - .and_modify(|c| *c += 1) - .or_insert(0) - .to_string(); - // Return `{count}.{ext}` as the new name, i.e. `0.cudafe1.stub.c` - count + extension - }) - .to_owned() - } - None => { - // If the argument isn't a file name with one of our extensions, - // it may _reference_ files we've renamed. Go through and replace - // all old names with their new stable names. - let mut arg = arg.clone(); - for (old, new) in old_to_new.iter() { - arg = arg.replace(old, new); - } - arg - } - } - }) - .collect::>() - } - - let mut nvcc_dryrun_cmd = creator.clone().new_command_sync(executable); - - nvcc_dryrun_cmd - .env_clear() - .args(arguments) - .envs(env_vars.to_vec()) - .current_dir(cwd); - - let nvcc_dryrun_output = run_input_output(nvcc_dryrun_cmd, None).await?; + // Run `nvcc --dryrun` twice to ensure the commands are correct + // relative to the directory where they're run. + // + // All the "nvcc" commands (cudafe++, cicc, ptxas, nvlink, fatbinary) + // are run in the temp dir, so their arguments should be relative to + // the temp dir, e.g. `cudafe++ [...] "x.cpp4.ii"` + // + // All the host compiler invocations are run in the original `cwd` where + // sccache was invoked. Arguments will be relative to the cwd, except + // any arguments that reference nvcc-generated files should be absolute + // to the temp dir, e.g. `gcc -E [...] x.cu -o /tmp/dir/x.cpp4.ii` + + // Roughly equivalent to: + // ```shell + // cat <(nvcc --dryrun --keep \ + // | nl -n ln -s ' ' -w 1 \ + // | grep -P "^[0-9]+ (cicc|ptxas|cudafe|nvlink|fatbinary)") \ + // \ + // <(nvcc --dryrun --keep --keep-dir /tmp/dir \ + // | nl -n ln -s ' ' -w 1 \ + // | grep -P -v "^[0-9]+ (cicc|ptxas|cudafe|nvlink|fatbinary)") \ + // \ + // | sort -k 1n + // ``` + + let mut env_vars_copy = env_vars.to_vec(); + let is_nvcc_exe = + |exe: &str| matches!(exe, "cicc" | "ptxas" | "cudafe++" | "nvlink" | "fatbinary"); + + let (nvcc_commands, host_commands) = futures::future::try_join( + // Get the nvcc compile command lines with paths relative to `tmp` + select_nvcc_subcommands( + creator, + executable, + cwd, + env_vars, + keep_dir.is_none(), + arguments, + is_nvcc_exe, + ), + // Get the host compile command lines with paths relative to `cwd` and absolute paths to `tmp` + select_nvcc_subcommands( + creator, + executable, + cwd, + &mut env_vars_copy, + keep_dir.is_none(), + &[arguments, &["--keep-dir".into(), tmp.into()][..]].concat(), + |exe| !is_nvcc_exe(exe), + ), + ) + .await?; + + // Now zip the two lists of commands again by sorting on original line index. + // Transform to tuples that include the dir in which each command should run. + let all_commands = nvcc_commands + .iter() + .map(|(idx, exe, args)| (idx, tmp, exe, args)) + .chain( + host_commands + .iter() + .map(|(idx, exe, args)| (idx, cwd, exe, args)), + ) + .sorted_by(|a, b| Ord::cmp(&a.0, &b.0)); - let remap_filenames = keep_dir.is_none(); - let is_valid_line_re = Regex::new(r"^#\$ (.*)$").unwrap(); - let is_envvar_line_re = Regex::new(r"^([_A-Z]+)=(.*)$").unwrap(); + // Create groups of commands that should be run sequential relative to each other, + // but can optionally be run in parallel to other groups if the user requested via + // `nvcc --threads`. let mut no_more_groups = false; let mut command_groups: Vec> = vec![]; - let mut old_to_new = HashMap::::new(); - let mut ext_counts = HashMap::::new(); - - let tmp_name = |name: &String| temp_dir.join(name).into_os_string().into_string().ok(); - - let reader = std::io::BufReader::new(&nvcc_dryrun_output.stderr[..]); - - for line in reader.lines() { - // Select lines that match the `#$ ` prefix from nvcc --dryrun - let line = select_valid_dryrun_lines(&is_valid_line_re, &line?)?; - let maybe_exe_and_args = - fold_env_vars_or_split_into_exe_and_args(&is_envvar_line_re, env_vars, cwd, &line); - let (exe, mut args) = match maybe_exe_and_args { - Some(exe_and_args) => exe_and_args, - None => continue, - }; - // Remap nvcc's generated file names to deterministic names - if remap_filenames { - args = remap_generated_filenames(&args, &mut old_to_new, &mut ext_counts); + for (_, dir, exe, args) in all_commands { + if log_enabled!(log::Level::Trace) { + trace!( + "transformed nvcc command: {:?}", + [ + &[exe.to_str().unwrap_or_default().to_string()][..], + &args[..] + ] + .concat() + .join(" ") + ); } // * cicc and ptxas are cacheable // * cudafe++ and fatbinary are not cacheable // * Run cudafe++, nvlink, cicc, ptxas, and fatbinary in `temp_dir` // * Run host preprocessing and compilation steps in `cwd` - let (dir, cacheable) = match exe.file_name().and_then(|s| s.to_str()) { - Some("cicc") | Some("ptxas") => (temp_dir, Cacheable::Yes), - Some("cudafe++") | Some("nvlink") => (temp_dir, Cacheable::No), + let cacheable = match exe.file_name().and_then(|s| s.to_str()) { + Some("cicc") | Some("ptxas") => Cacheable::Yes, + Some("cudafe++") | Some("nvlink") => Cacheable::No, Some("fatbinary") => { // The fatbinary command represents the start of the last group if !no_more_groups { command_groups.push(vec![]); } no_more_groups = true; - (temp_dir, Cacheable::No) + Cacheable::No } - // Otherwise this potentially a host compiler command _ => { // All generated host compiler commands include `-D__CUDA_ARCH_LIST__=`. // If this definition isn't present, this command is either a new binary @@ -776,31 +672,17 @@ where .any(|arg| arg.starts_with("-D__CUDA_ARCH_LIST__")) { continue; - } - if args.contains(&"-E".to_owned()) { - // Each preprocessor step represents the start of a new command group + } else if args.contains(&"-E".to_owned()) { + // Each preprocessor step represents the start of a new command + // group, unless it comes after a call to fatbinary. if !no_more_groups { command_groups.push(vec![]); } - // Rewrite the output path of the preprocessor result to be the tempdir, - // not the cwd where sccache was invoked. cudafe++ embeds the path to the - // device stub as an #include in the file that produces the final object, - // so it must be a deterministic path and name to get cache hits. - let idx = args.len() - 1; - if let Some(path) = args.get(idx).and_then(tmp_name) { - args[idx] = path; - } - // Do not run preprocessor calls through sccache - (cwd, Cacheable::No) - } else { - // Rewrite the input path of the file compiled by the host compiler into - // the final object. This is the other half of the process described above. - let idx = args.len() - 3; - if let Some(path) = args.get(idx).and_then(tmp_name) { - args[idx] = path; - } - (cwd, Cacheable::Yes) } + // Do not cache host compiler calls, since the output will + // be cached as the result of the outer `nvcc` command. + // Caching this would just store the same object twice. + Cacheable::No } }; @@ -814,8 +696,8 @@ where None => {} Some(group) => { group.push(NvccGeneratedSubcommand { - exe, - args, + exe: exe.clone(), + args: args.clone(), cwd: dir.into(), cacheable, }); @@ -826,6 +708,222 @@ where Ok(command_groups) } +async fn select_nvcc_subcommands( + creator: &T, + executable: &Path, + cwd: &Path, + env_vars: &mut Vec<(OsString, OsString)>, + remap_filenames: bool, + arguments: &[OsString], + select_subcommand: F, +) -> Result)>> +where + F: Fn(&str) -> bool, + T: CommandCreatorSync, +{ + if log_enabled!(log::Level::Trace) { + trace!( + "nvcc dryrun command: {:?}", + [ + &[executable.to_str().unwrap_or_default().to_string()][..], + &dist::osstrings_to_strings(arguments).unwrap_or_default()[..], + &["--dryrun".into(), "--keep".into()][..] + ] + .concat() + .join(" ") + ); + } + + let mut nvcc_dryrun_cmd = creator.clone().new_command_sync(executable); + + nvcc_dryrun_cmd + .args(&[arguments, &["--dryrun".into(), "--keep".into()][..]].concat()) + .env_clear() + .current_dir(cwd) + .envs(env_vars.to_vec()); + + let nvcc_dryrun_output = run_input_output(nvcc_dryrun_cmd, None).await?; + + let mut ext_counts = HashMap::::new(); + let mut old_to_new = HashMap::::new(); + let is_valid_line_re = Regex::new(r"^#\$ (.*)$").unwrap(); + let is_envvar_line_re = Regex::new(r"^([_A-Z]+)=(.*)$").unwrap(); + + let mut lines = Vec::<(usize, PathBuf, Vec)>::new(); + let reader = std::io::BufReader::new(&nvcc_dryrun_output.stderr[..]); + + for pair in reader.lines().enumerate() { + let (idx, line) = pair; + // Select lines that match the `#$ ` prefix from nvcc --dryrun + let line = select_valid_dryrun_lines(&is_valid_line_re, &line?)?; + + let maybe_exe_and_args = + fold_env_vars_or_split_into_exe_and_args(&is_envvar_line_re, env_vars, cwd, &line); + + let (exe, mut args) = match maybe_exe_and_args { + Some(exe_and_args) => exe_and_args, + None => continue, + }; + + // Remap nvcc's generated file names to deterministic names + if remap_filenames { + args = remap_generated_filenames(&args, &mut old_to_new, &mut ext_counts); + } + + match exe.file_name().and_then(|s| s.to_str()) { + None => continue, + Some(exe_name) => { + if select_subcommand(exe_name) { + lines.push((idx, exe, args)); + } + } + } + } + + Ok(lines) +} + +fn select_valid_dryrun_lines(re: &Regex, line: &str) -> Result { + match re.captures(line) { + Some(caps) => { + let (_, [rest]) = caps.extract(); + Ok(rest.to_string()) + } + _ => Err(anyhow!("nvcc error: {:?}", line)), + } +} + +fn fold_env_vars_or_split_into_exe_and_args( + re: &Regex, + env_vars: &mut Vec<(OsString, OsString)>, + cwd: &Path, + line: &str, +) -> Option<(PathBuf, Vec)> { + // Intercept the environment variable lines and add them to the env_vars list + if let Some(var) = re.captures(line) { + let (_, [var, val]) = var.extract(); + + let loc = if let Some(idx) = env_vars.iter().position(|(key, _)| key == var) { + idx..idx + 1 + } else { + env_vars.len()..env_vars.len() + }; + + let mut pair = (var.into(), val.into()); + // Handle the special `_SPACE_= ` line + if val != " " { + pair.1 = val + .trim() + .split(' ') + .map(|x| x.trim_start_matches('\"').trim_end_matches('\"')) + .collect::>() + .join(" ") + .into(); + } + env_vars.splice(loc, [pair]); + return None; + } + + // The rest of the lines are subcommands, so parse into a vec of [cmd, args..] + + let mut line = line.to_owned(); + + // Expand envvars in nvcc subcommands, i.e. "$CICC_PATH/cicc ..." + if let Some(env_vars) = dist::osstring_tuples_to_strings(env_vars) { + for (key, val) in env_vars { + let var = "$".to_owned() + &key; + line = line.replace(&var, &val); + } + } + + let args = shlex::split(&line)?; + let (exe, args) = args.split_first()?; + + let env_path = env_vars + .iter() + .find(|(k, _)| k == "PATH") + .map(|(_, p)| p.to_owned()) + .unwrap(); + + if let Ok(exe) = which_in(exe, env_path.into(), cwd) { + return Some((exe.clone(), args.to_vec())); + } + + None +} + +fn remap_generated_filenames( + args: &[String], + old_to_new: &mut HashMap, + ext_counts: &mut HashMap, +) -> Vec { + args.iter() + .map(|arg| { + // If the argument doesn't start with `-` and is a file that + // ends in one of the below extensions, rename the file to an + // auto-incrementing stable name + let maybe_extension = (!arg.starts_with('-')) + .then(|| { + [ + ".cpp1.ii", + ".cpp4.ii", + ".cudafe1.c", + ".cudafe1.cpp", + ".cudafe1.stub.c", + ] + .iter() + .find(|ext| arg.ends_with(*ext)) + .copied() + }) + .unwrap_or(None); + + // If the argument is a file that ends in one of the above extensions: + // * If it's our first time seeing this file, create a unique name for it + // * If we've seen this file before, lookup its unique name in the hash map + // + // This ensures stable names are in cudafe++ output and #include directives, + // eliminating one source of false-positive cache misses. + match maybe_extension { + Some(extension) => { + old_to_new + .entry(arg.into()) + .or_insert_with(|| { + // Initialize or update the number of files with a given extension: + // compute_70.cudafe1.stub.c -> 0.cudafe1.stub.c + // compute_60.cudafe1.stub.c -> 1.cudafe1.stub.c + // etc. + let count = ext_counts + .entry(extension.into()) + .and_modify(|c| *c += 1) + .or_insert(0) + .to_string(); + // Return `/tmp/dir/{count}.{ext}` as the new name, i.e. `/tmp/dir/0.cudafe1.stub.c` + PathBuf::from(arg) + .parent() + .unwrap_or(Path::new("")) + .join(count + extension) + .as_os_str() + .to_str() + .unwrap_or("") + .to_owned() + }) + .to_owned() + } + None => { + // If the argument isn't a file name with one of our extensions, + // it may _reference_ files we've renamed. Go through and replace + // all old names with their new stable names. + let mut arg = arg.clone(); + for (old, new) in old_to_new.iter() { + arg = arg.replace(old, new); + } + arg + } + } + }) + .collect::>() +} + async fn run_nvcc_subcommands_group( service: &server::SccacheService, creator: &T, diff --git a/tests/system.rs b/tests/system.rs index 18cde37819..d390f0e22c 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -587,13 +587,12 @@ fn test_cuda_compiles(compiler: &Compiler, tempdir: &Path) { trace!("compile A request stats"); get_stats(|info| { assert_eq!(1, info.stats.compile_requests); - assert_eq!(4, info.stats.requests_executed); + assert_eq!(3, info.stats.requests_executed); assert_eq!(0, info.stats.cache_hits.all()); - assert_eq!(4, info.stats.cache_misses.all()); + assert_eq!(3, info.stats.cache_misses.all()); assert_eq!(&1, info.stats.cache_misses.get("CUDA").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("PTX").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("CUBIN").unwrap()); - assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); let adv_cuda_key = adv_key_kind("cuda", compiler.name); let adv_ptx_key = adv_key_kind("ptx", compiler.name); let adv_cubin_key = adv_key_kind("cubin", compiler.name); @@ -622,16 +621,15 @@ fn test_cuda_compiles(compiler: &Compiler, tempdir: &Path) { trace!("compile A request stats"); get_stats(|info| { assert_eq!(2, info.stats.compile_requests); - assert_eq!(5, info.stats.requests_executed); + assert_eq!(4, info.stats.requests_executed); assert_eq!(1, info.stats.cache_hits.all()); - assert_eq!(4, info.stats.cache_misses.all()); + assert_eq!(3, info.stats.cache_misses.all()); assert_eq!(&1, info.stats.cache_hits.get("CUDA").unwrap()); assert!(info.stats.cache_hits.get("PTX").is_none()); assert!(info.stats.cache_hits.get("CUBIN").is_none()); assert_eq!(&1, info.stats.cache_misses.get("CUDA").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("PTX").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("CUBIN").unwrap()); - assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); let adv_cuda_key = adv_key_kind("cuda", compiler.name); let adv_ptx_key = adv_key_kind("ptx", compiler.name); let adv_cubin_key = adv_key_kind("cubin", compiler.name); @@ -665,16 +663,15 @@ fn test_cuda_compiles(compiler: &Compiler, tempdir: &Path) { trace!("compile B request stats"); get_stats(|info| { assert_eq!(3, info.stats.compile_requests); - assert_eq!(9, info.stats.requests_executed); + assert_eq!(7, info.stats.requests_executed); assert_eq!(2, info.stats.cache_hits.all()); - assert_eq!(7, info.stats.cache_misses.all()); + assert_eq!(5, info.stats.cache_misses.all()); assert_eq!(&1, info.stats.cache_hits.get("CUDA").unwrap()); assert!(info.stats.cache_hits.get("PTX").is_none()); assert_eq!(&1, info.stats.cache_hits.get("CUBIN").unwrap()); assert_eq!(&2, info.stats.cache_misses.get("CUDA").unwrap()); assert_eq!(&2, info.stats.cache_misses.get("PTX").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("CUBIN").unwrap()); - assert_eq!(&2, info.stats.cache_misses.get("C/C++").unwrap()); let adv_cuda_key = adv_key_kind("cuda", compiler.name); let adv_ptx_key = adv_key_kind("ptx", compiler.name); let adv_cubin_key = adv_key_kind("cubin", compiler.name); @@ -707,16 +704,15 @@ fn test_cuda_compiles(compiler: &Compiler, tempdir: &Path) { trace!("compile ptx request stats"); get_stats(|info| { assert_eq!(4, info.stats.compile_requests); - assert_eq!(11, info.stats.requests_executed); + assert_eq!(9, info.stats.requests_executed); assert_eq!(3, info.stats.cache_hits.all()); - assert_eq!(8, info.stats.cache_misses.all()); + assert_eq!(6, info.stats.cache_misses.all()); assert_eq!(&1, info.stats.cache_hits.get("CUDA").unwrap()); assert_eq!(&1, info.stats.cache_hits.get("PTX").unwrap()); assert_eq!(&1, info.stats.cache_hits.get("CUBIN").unwrap()); assert_eq!(&3, info.stats.cache_misses.get("CUDA").unwrap()); assert_eq!(&2, info.stats.cache_misses.get("PTX").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("CUBIN").unwrap()); - assert_eq!(&2, info.stats.cache_misses.get("C/C++").unwrap()); let adv_cuda_key = adv_key_kind("cuda", compiler.name); let adv_ptx_key = adv_key_kind("ptx", compiler.name); let adv_cubin_key = adv_key_kind("cubin", compiler.name); @@ -749,16 +745,15 @@ fn test_cuda_compiles(compiler: &Compiler, tempdir: &Path) { trace!("compile cubin request stats"); get_stats(|info| { assert_eq!(5, info.stats.compile_requests); - assert_eq!(14, info.stats.requests_executed); + assert_eq!(12, info.stats.requests_executed); assert_eq!(5, info.stats.cache_hits.all()); - assert_eq!(9, info.stats.cache_misses.all()); + assert_eq!(7, info.stats.cache_misses.all()); assert_eq!(&1, info.stats.cache_hits.get("CUDA").unwrap()); assert_eq!(&2, info.stats.cache_hits.get("PTX").unwrap()); assert_eq!(&2, info.stats.cache_hits.get("CUBIN").unwrap()); assert_eq!(&4, info.stats.cache_misses.get("CUDA").unwrap()); assert_eq!(&2, info.stats.cache_misses.get("PTX").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("CUBIN").unwrap()); - assert_eq!(&2, info.stats.cache_misses.get("C/C++").unwrap()); let adv_cuda_key = adv_key_kind("cuda", compiler.name); let adv_ptx_key = adv_key_kind("ptx", compiler.name); let adv_cubin_key = adv_key_kind("cubin", compiler.name); @@ -833,13 +828,13 @@ fn test_proper_lang_stat_tracking(compiler: Compiler, tempdir: &Path) { trace!("request stats"); get_stats(|info| { assert_eq!(4, info.stats.compile_requests); - assert_eq!(8, info.stats.requests_executed); + assert_eq!(6, info.stats.requests_executed); assert_eq!(3, info.stats.cache_hits.all()); - assert_eq!(5, info.stats.cache_misses.all()); + assert_eq!(3, info.stats.cache_misses.all()); assert_eq!(&1, info.stats.cache_hits.get("C/C++").unwrap()); assert_eq!(&1, info.stats.cache_hits.get("CUDA").unwrap()); assert_eq!(&1, info.stats.cache_hits.get("CUBIN").unwrap()); - assert_eq!(&3, info.stats.cache_misses.get("C/C++").unwrap()); + assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("CUDA").unwrap()); assert_eq!(&1, info.stats.cache_misses.get("PTX").unwrap()); });