mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-01-08 21:58:00 -05:00
Generalize block filter in Adapter (#3133)
As per @Schaeff's comment https://github.com/powdr-labs/powdr/pull/3124#issuecomment-3144116469. This can be orthogonal with #3124, which this PR can apply on top of. Alternatively, if we don't want the CLI option of max number of basic block instructions, we can apply this PR without #3124.
This commit is contained in:
@@ -6,7 +6,7 @@ use powdr_number::FieldElement;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
blocks::{Candidate, Instruction, Program},
|
||||
blocks::{BasicBlock, Candidate, Instruction, Program},
|
||||
constraint_optimizer::IsBusStateful,
|
||||
memory_optimizer::MemoryBusInteraction,
|
||||
Apc, InstructionHandler, VmConfig,
|
||||
@@ -32,6 +32,10 @@ pub trait Adapter: Sized {
|
||||
fn into_field(e: Self::PowdrField) -> Self::Field;
|
||||
|
||||
fn from_field(e: Self::Field) -> Self::PowdrField;
|
||||
|
||||
fn should_skip_block(_block: &BasicBlock<Self::Instruction>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub type ApcStats<A> = <<A as Adapter>::Candidate as Candidate<A>>::ApcStats;
|
||||
|
||||
@@ -23,8 +23,7 @@ pub enum PgoConfig {
|
||||
/// value = cells saved per apc * times executed
|
||||
/// cost = number of columns in the apc
|
||||
/// constraint of max total columns
|
||||
/// constraint of max number of instructions per block
|
||||
Cell(HashMap<u64, u32>, Option<usize>, Option<usize>),
|
||||
Cell(HashMap<u64, u32>, Option<usize>),
|
||||
/// value = instruction per apc * times executed
|
||||
Instruction(HashMap<u64, u32>),
|
||||
/// value = instruction per apc
|
||||
@@ -36,7 +35,7 @@ impl PgoConfig {
|
||||
/// Returns the number of times a certain pc was executed in the profile.
|
||||
pub fn pc_execution_count(&self, pc: u64) -> Option<u32> {
|
||||
match self {
|
||||
PgoConfig::Cell(pc_count, _, _) | PgoConfig::Instruction(pc_count) => {
|
||||
PgoConfig::Cell(pc_count, _) | PgoConfig::Instruction(pc_count) => {
|
||||
pc_count.get(&pc).copied()
|
||||
}
|
||||
PgoConfig::None => None,
|
||||
@@ -60,11 +59,10 @@ pub enum PgoType {
|
||||
pub fn pgo_config(
|
||||
pgo: PgoType,
|
||||
max_columns: Option<usize>,
|
||||
max_block_instructions: Option<usize>,
|
||||
execution_profile: HashMap<u64, u32>,
|
||||
) -> PgoConfig {
|
||||
match pgo {
|
||||
PgoType::Cell => PgoConfig::Cell(execution_profile, max_columns, max_block_instructions),
|
||||
PgoType::Cell => PgoConfig::Cell(execution_profile, max_columns),
|
||||
PgoType::Instruction => PgoConfig::Instruction(execution_profile),
|
||||
PgoType::None => PgoConfig::None,
|
||||
}
|
||||
@@ -118,7 +116,6 @@ fn create_apcs_with_cell_pgo<A: Adapter>(
|
||||
pgo_program_pc_count: HashMap<u64, u32>,
|
||||
config: &PowdrConfig,
|
||||
max_total_apc_columns: Option<usize>,
|
||||
max_block_instructions: Option<usize>,
|
||||
vm_config: AdapterVmConfig<A>,
|
||||
) -> Vec<(AdapterApc<A>, ApcStats<A>)> {
|
||||
if config.autoprecompiles == 0 {
|
||||
@@ -130,11 +127,6 @@ fn create_apcs_with_cell_pgo<A: Adapter>(
|
||||
// Also only keep basic blocks with more than one original instruction.
|
||||
blocks.retain(|b| pgo_program_pc_count.contains_key(&b.start_pc) && b.statements.len() > 1);
|
||||
|
||||
// drop any block with more than max_block_instructions instructions
|
||||
if let Some(max_block_instructions) = max_block_instructions {
|
||||
blocks.retain(|b| b.statements.len() <= max_block_instructions);
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"Retained {} basic blocks after filtering by pc_idx_count",
|
||||
blocks.len()
|
||||
@@ -247,30 +239,30 @@ fn create_apcs_with_no_pgo<A: Adapter>(
|
||||
}
|
||||
|
||||
pub fn generate_apcs_with_pgo<A: Adapter>(
|
||||
blocks: Vec<BasicBlock<A::Instruction>>,
|
||||
mut blocks: Vec<BasicBlock<A::Instruction>>,
|
||||
config: &PowdrConfig,
|
||||
max_total_apc_columns: Option<usize>,
|
||||
pgo_config: PgoConfig,
|
||||
vm_config: AdapterVmConfig<A>,
|
||||
) -> Vec<(AdapterApc<A>, Option<ApcStats<A>>)> {
|
||||
// filter out blocks that should be skipped according to the adapter
|
||||
blocks.retain(|block| !A::should_skip_block(block));
|
||||
|
||||
// sort basic blocks by:
|
||||
// 1. if PgoConfig::Cell, cost = frequency * cells_saved_per_row
|
||||
// 2. if PgoConfig::Instruction, cost = frequency * number_of_instructions
|
||||
// 3. if PgoConfig::None, cost = number_of_instructions
|
||||
let res: Vec<_> = match pgo_config {
|
||||
PgoConfig::Cell(pgo_program_idx_count, _, max_block_instructions) => {
|
||||
create_apcs_with_cell_pgo::<A>(
|
||||
blocks,
|
||||
pgo_program_idx_count,
|
||||
config,
|
||||
max_total_apc_columns,
|
||||
max_block_instructions,
|
||||
vm_config,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|(apc, apc_stats)| (apc, Some(apc_stats)))
|
||||
.collect()
|
||||
}
|
||||
PgoConfig::Cell(pgo_program_idx_count, _) => create_apcs_with_cell_pgo::<A>(
|
||||
blocks,
|
||||
pgo_program_idx_count,
|
||||
config,
|
||||
max_total_apc_columns,
|
||||
vm_config,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|(apc, apc_stats)| (apc, Some(apc_stats)))
|
||||
.collect(),
|
||||
PgoConfig::Instruction(pgo_program_idx_count) => {
|
||||
create_apcs_with_instruction_pgo::<A>(blocks, pgo_program_idx_count, config, vm_config)
|
||||
.into_iter()
|
||||
|
||||
@@ -41,10 +41,6 @@ enum Commands {
|
||||
#[clap(long)]
|
||||
max_columns: Option<usize>,
|
||||
|
||||
/// When `--pgo-mode cell`, the optional max number of instructions per block
|
||||
#[arg(long)]
|
||||
max_block_instructions: Option<usize>,
|
||||
|
||||
#[arg(long)]
|
||||
input: Option<u32>,
|
||||
|
||||
@@ -69,10 +65,6 @@ enum Commands {
|
||||
#[clap(long)]
|
||||
max_columns: Option<usize>,
|
||||
|
||||
/// When `--pgo-mode cell`, the optional max number of instructions per block
|
||||
#[arg(long)]
|
||||
max_block_instructions: Option<usize>,
|
||||
|
||||
#[arg(long)]
|
||||
input: Option<u32>,
|
||||
|
||||
@@ -105,10 +97,6 @@ enum Commands {
|
||||
#[clap(long)]
|
||||
max_columns: Option<usize>,
|
||||
|
||||
/// When `--pgo-mode cell`, the optional max number of instructions per block
|
||||
#[arg(long)]
|
||||
max_block_instructions: Option<usize>,
|
||||
|
||||
#[arg(long)]
|
||||
input: Option<u32>,
|
||||
|
||||
@@ -143,7 +131,6 @@ fn run_command(command: Commands) {
|
||||
skip,
|
||||
pgo,
|
||||
max_columns,
|
||||
max_block_instructions,
|
||||
input,
|
||||
apc_candidates_dir,
|
||||
} => {
|
||||
@@ -156,8 +143,7 @@ fn run_command(command: Commands) {
|
||||
guest_opts.clone(),
|
||||
stdin_from(input),
|
||||
);
|
||||
let pgo_config =
|
||||
pgo_config(pgo, max_columns, max_block_instructions, execution_profile);
|
||||
let pgo_config = pgo_config(pgo, max_columns, execution_profile);
|
||||
let program = powdr_openvm::compile_guest(
|
||||
&guest,
|
||||
guest_opts,
|
||||
@@ -175,7 +161,6 @@ fn run_command(command: Commands) {
|
||||
skip,
|
||||
pgo,
|
||||
max_columns,
|
||||
max_block_instructions,
|
||||
input,
|
||||
apc_candidates_dir,
|
||||
} => {
|
||||
@@ -188,8 +173,7 @@ fn run_command(command: Commands) {
|
||||
guest_opts.clone(),
|
||||
stdin_from(input),
|
||||
);
|
||||
let pgo_config =
|
||||
pgo_config(pgo, max_columns, max_block_instructions, execution_profile);
|
||||
let pgo_config = pgo_config(pgo, max_columns, execution_profile);
|
||||
let program = powdr_openvm::compile_guest(
|
||||
&guest,
|
||||
guest_opts,
|
||||
@@ -209,7 +193,6 @@ fn run_command(command: Commands) {
|
||||
recursion,
|
||||
pgo,
|
||||
max_columns,
|
||||
max_block_instructions,
|
||||
input,
|
||||
metrics,
|
||||
apc_candidates_dir,
|
||||
@@ -223,8 +206,7 @@ fn run_command(command: Commands) {
|
||||
guest_opts.clone(),
|
||||
stdin_from(input),
|
||||
);
|
||||
let pgo_config =
|
||||
pgo_config(pgo, max_columns, max_block_instructions, execution_profile);
|
||||
let pgo_config = pgo_config(pgo, max_columns, execution_profile);
|
||||
let program = powdr_openvm::compile_guest(
|
||||
&guest,
|
||||
guest_opts,
|
||||
|
||||
@@ -173,7 +173,7 @@ pub fn customize(
|
||||
};
|
||||
|
||||
let max_total_apc_columns: Option<usize> = match pgo_config {
|
||||
PgoConfig::Cell(_, max_total_columns, _) => max_total_columns.map(|max_total_columns| {
|
||||
PgoConfig::Cell(_, max_total_columns) => max_total_columns.map(|max_total_columns| {
|
||||
let total_non_apc_columns = original_config
|
||||
.chip_inventory_air_metrics()
|
||||
.values()
|
||||
|
||||
@@ -301,8 +301,7 @@ fn instruction_index_to_pc(program: &Program<BabyBear>, idx: usize) -> u64 {
|
||||
|
||||
fn tally_opcode_frequency(pgo_config: &PgoConfig, exe: &VmExe<BabyBear>) {
|
||||
let pgo_program_pc_count = match pgo_config {
|
||||
PgoConfig::Cell(pgo_program_pc_count, _, _)
|
||||
| PgoConfig::Instruction(pgo_program_pc_count) => {
|
||||
PgoConfig::Cell(pgo_program_pc_count, _) | PgoConfig::Instruction(pgo_program_pc_count) => {
|
||||
// If execution count of each pc is available, we tally the opcode execution frequency
|
||||
tracing::debug!("Opcode execution frequency:");
|
||||
pgo_program_pc_count
|
||||
@@ -956,7 +955,7 @@ mod tests {
|
||||
config.clone(),
|
||||
PrecompileImplementation::SingleRowChip,
|
||||
stdin,
|
||||
PgoConfig::Cell(pgo_data, None, None),
|
||||
PgoConfig::Cell(pgo_data, None),
|
||||
None,
|
||||
);
|
||||
}
|
||||
@@ -1048,7 +1047,7 @@ mod tests {
|
||||
config.clone(),
|
||||
PrecompileImplementation::SingleRowChip,
|
||||
stdin.clone(),
|
||||
PgoConfig::Cell(pgo_data.clone(), None, None),
|
||||
PgoConfig::Cell(pgo_data.clone(), None),
|
||||
None,
|
||||
);
|
||||
let elapsed = start.elapsed();
|
||||
@@ -1134,7 +1133,7 @@ mod tests {
|
||||
config.clone(),
|
||||
PrecompileImplementation::SingleRowChip,
|
||||
stdin,
|
||||
PgoConfig::Cell(pgo_data, None, None),
|
||||
PgoConfig::Cell(pgo_data, None),
|
||||
None,
|
||||
);
|
||||
}
|
||||
@@ -1213,7 +1212,7 @@ mod tests {
|
||||
config.clone(),
|
||||
PrecompileImplementation::SingleRowChip,
|
||||
stdin.clone(),
|
||||
PgoConfig::Cell(pgo_data.clone(), None, None),
|
||||
PgoConfig::Cell(pgo_data.clone(), None),
|
||||
None,
|
||||
);
|
||||
let elapsed = start.elapsed();
|
||||
@@ -1287,7 +1286,7 @@ mod tests {
|
||||
let apc_candidates_dir_path = apc_candidates_dir.path();
|
||||
let config = default_powdr_openvm_config(guest.apc, guest.skip)
|
||||
.with_apc_candidates_dir(apc_candidates_dir_path);
|
||||
let is_cell_pgo = matches!(guest.pgo_config, PgoConfig::Cell(_, _, _));
|
||||
let is_cell_pgo = matches!(guest.pgo_config, PgoConfig::Cell(_, _));
|
||||
let compiled_program = compile_guest(
|
||||
guest.name,
|
||||
GuestOptions::default(),
|
||||
@@ -1400,7 +1399,7 @@ mod tests {
|
||||
|
||||
test_machine_compilation(
|
||||
GuestTestConfig {
|
||||
pgo_config: PgoConfig::Cell(pgo_data, None, None),
|
||||
pgo_config: PgoConfig::Cell(pgo_data, None),
|
||||
name: GUEST,
|
||||
apc: GUEST_APC,
|
||||
skip: GUEST_SKIP_PGO,
|
||||
@@ -1476,7 +1475,7 @@ mod tests {
|
||||
|
||||
test_machine_compilation(
|
||||
GuestTestConfig {
|
||||
pgo_config: PgoConfig::Cell(pgo_data, None, None),
|
||||
pgo_config: PgoConfig::Cell(pgo_data, None),
|
||||
name: GUEST_SHA256,
|
||||
apc: GUEST_SHA256_APC_PGO,
|
||||
skip: GUEST_SHA256_SKIP,
|
||||
@@ -1611,7 +1610,7 @@ mod tests {
|
||||
|
||||
test_machine_compilation(
|
||||
GuestTestConfig {
|
||||
pgo_config: PgoConfig::Cell(pgo_data, None, None),
|
||||
pgo_config: PgoConfig::Cell(pgo_data, None),
|
||||
name: GUEST_KECCAK,
|
||||
apc: GUEST_KECCAK_APC,
|
||||
skip: GUEST_KECCAK_SKIP,
|
||||
@@ -1662,7 +1661,7 @@ mod tests {
|
||||
|
||||
test_machine_compilation(
|
||||
GuestTestConfig {
|
||||
pgo_config: PgoConfig::Cell(pgo_data, Some(MAX_TOTAL_COLUMNS), None),
|
||||
pgo_config: PgoConfig::Cell(pgo_data, Some(MAX_TOTAL_COLUMNS)),
|
||||
name: GUEST_KECCAK,
|
||||
apc: GUEST_KECCAK_APC_PGO_LARGE,
|
||||
skip: GUEST_KECCAK_SKIP,
|
||||
|
||||
Reference in New Issue
Block a user