-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Profiling] Multi-component programs (#2269)
This PR deals with profiling multi-component programs. It contains: - Modifications to TDCC to produce a single json after _processing the whole program_ (rather than outputting JSON after each component) - A `component-cells` tool that produces information about the user-defined component cells (so, no primitives and no constant cells) in each component. - Modifications to existing profiling scripts to take multi-component programs into account. I also cleaned the scripts up a bit and left more documentation in the comments. The component-cells backend is necessary because the instantiation of a component is used in the VCD, but we need to know the name of the original component to fetch the groups that could execute in that cell. i.e., in [`multi-component.futil`](https://github.com/calyxir/calyx/blob/main/examples/futil/multi-component.futil), we have `id`, which is an instantiation of the `identity()` component. The `go` signal for the `save` group would subsequently be `TOP.TOP.main.id.save_go...`. So, we need some way of relating that there is a `id` cell that instantiates `identity` within the `main` component. Any and all feedback would be appreciated :) Additionally, if anyone has better names for `component-cells` (or the labels in the JSON, in the Usage section below), it would be very helpful! (Tagging @ekiwi here since he wasn't in the list of suggested reviewers) We are now in a nice place where we can start profiling (non-optimized) Calyx programs! My next step is to try the profiler on some "real-life" programs (maybe the benchmarks on the performance dashboard). ## Usage for `component-cells` tool The tool lives in `tools/component_cells/`, so to run: ex) ``` cargo run --manifest-path tools/component_cells/Cargo.toml examples/futil/multi-component.futil ``` will produce ``` [ { "component": "main", "is_main_component": true, "cell_info": [ { "cell_name": "id", "component_name": "identity" } ] }, { "component": "identity", "is_main_component": false, "cell_info": [] } ] ``` indicating that (1) `main` is the main/entry point component, (2) `main` contains a cell named `id` that instantiates the `identity` component, (3) the `identity` component doesn't have any (non-primitive, non-constant) cells.
- Loading branch information
1 parent
176da7a
commit 0ed827f
Showing
9 changed files
with
478 additions
and
83 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
[package] | ||
name = "component_cells" | ||
authors.workspace = true | ||
license-file.workspace = true | ||
keywords.workspace = true | ||
repository.workspace = true | ||
readme.workspace = true | ||
description.workspace = true | ||
categories.workspace = true | ||
homepage.workspace = true | ||
edition.workspace = true | ||
version.workspace = true | ||
rust-version.workspace = true | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
serde.workspace = true | ||
argh.workspace = true | ||
serde_json = "1.0.79" | ||
|
||
calyx-utils = { path = "../../calyx-utils" } | ||
calyx-frontend = { path = "../../calyx-frontend" } | ||
calyx-opt = { path = "../../calyx-opt" } | ||
|
||
[dependencies.calyx-ir] | ||
path = "../../calyx-ir" | ||
features = ["serialize"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
use argh::FromArgs; | ||
use calyx_frontend as frontend; | ||
use calyx_ir::{self as ir, Id}; | ||
use calyx_utils::{CalyxResult, OutputFile}; | ||
use serde::Serialize; | ||
use std::path::{Path, PathBuf}; | ||
use std::{collections::HashSet, io}; | ||
|
||
#[derive(FromArgs)] | ||
/// Path for library and path for file to read from | ||
struct Args { | ||
/// file path to read data from | ||
#[argh(positional, from_str_fn(read_path))] | ||
file_path: Option<PathBuf>, | ||
|
||
/// library path | ||
#[argh(option, short = 'l', default = "Path::new(\".\").into()")] | ||
pub lib_path: PathBuf, | ||
|
||
/// output file | ||
#[argh(option, short = 'o', default = "OutputFile::Stdout")] | ||
pub output: OutputFile, | ||
} | ||
|
||
fn read_path(path: &str) -> Result<PathBuf, String> { | ||
Ok(Path::new(path).into()) | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct ComponentCellsBackend; | ||
|
||
fn main() -> CalyxResult<()> { | ||
let p: Args = argh::from_env(); | ||
|
||
let ws = frontend::Workspace::construct(&p.file_path, &p.lib_path)?; | ||
|
||
let ctx: ir::Context = ir::from_ast::ast_to_ir(ws)?; | ||
|
||
let main_comp = ctx.entrypoint(); | ||
|
||
let mut component_info: HashSet<ComponentInfo> = HashSet::new(); | ||
gen_component_info(&ctx, main_comp, true, &mut component_info); | ||
write_json(component_info.clone(), p.output)?; | ||
Ok(()) | ||
} | ||
|
||
fn id_serialize_passthrough<S>(id: &Id, ser: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: serde::Serializer, | ||
{ | ||
id.to_string().serialize(ser) | ||
} | ||
|
||
#[derive(PartialEq, Eq, Hash, Clone, Serialize)] | ||
struct ComponentInfo { | ||
#[serde(serialize_with = "id_serialize_passthrough")] | ||
pub component: Id, | ||
pub is_main_component: bool, | ||
pub cell_info: Vec<ComponentCellInfo>, | ||
} | ||
|
||
#[derive(PartialEq, Eq, Hash, Clone, Serialize)] | ||
struct ComponentCellInfo { | ||
#[serde(serialize_with = "id_serialize_passthrough")] | ||
pub cell_name: Id, | ||
#[serde(serialize_with = "id_serialize_passthrough")] | ||
pub component_name: Id, | ||
} | ||
|
||
/// Accumulates a set of components to the cells that they contain | ||
/// in the program with entrypoint `main_comp`. The contained cells | ||
/// are denoted with the name of the cell and the name of the component | ||
/// the cell is associated with. | ||
fn gen_component_info( | ||
ctx: &ir::Context, | ||
comp: &ir::Component, | ||
is_main_comp: bool, | ||
component_info: &mut HashSet<ComponentInfo>, | ||
) { | ||
let mut curr_comp_info = ComponentInfo { | ||
component: comp.name, | ||
is_main_component: is_main_comp, | ||
cell_info: Vec::new(), | ||
}; | ||
for cell in comp.cells.iter() { | ||
let cell_ref = cell.borrow(); | ||
if let ir::CellType::Component { name } = cell_ref.prototype { | ||
curr_comp_info.cell_info.push(ComponentCellInfo { | ||
cell_name: cell_ref.name(), | ||
component_name: name, | ||
}); | ||
let component = ctx | ||
.components | ||
.iter() | ||
.find(|comp| comp.name == name) | ||
.unwrap(); | ||
gen_component_info(ctx, component, false, component_info); | ||
} | ||
} | ||
component_info.insert(curr_comp_info); | ||
} | ||
|
||
/// Write the collected set of component information to a JSON file. | ||
fn write_json( | ||
component_info: HashSet<ComponentInfo>, | ||
file: OutputFile, | ||
) -> Result<(), io::Error> { | ||
let created_vec: Vec<ComponentInfo> = component_info.into_iter().collect(); | ||
serde_json::to_writer_pretty(file.get_write(), &created_vec)?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
[package] | ||
name = "component_groups" | ||
authors.workspace = true | ||
license-file.workspace = true | ||
keywords.workspace = true | ||
repository.workspace = true | ||
readme.workspace = true | ||
description.workspace = true | ||
categories.workspace = true | ||
homepage.workspace = true | ||
edition.workspace = true | ||
version.workspace = true | ||
rust-version.workspace = true | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
serde.workspace = true | ||
argh.workspace = true | ||
serde_json = "1.0.79" | ||
|
||
calyx-utils = { path = "../../calyx-utils" } | ||
calyx-frontend = { path = "../../calyx-frontend" } | ||
calyx-opt = { path = "../../calyx-opt" } | ||
|
||
[dependencies.calyx-ir] | ||
path = "../../calyx-ir" | ||
features = ["serialize"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
use argh::FromArgs; | ||
use calyx_frontend as frontend; | ||
use calyx_ir::{self as ir, Id}; | ||
use calyx_utils::{CalyxResult, OutputFile}; | ||
use serde::Serialize; | ||
use std::path::{Path, PathBuf}; | ||
use std::{collections::HashSet, io}; | ||
|
||
#[derive(FromArgs)] | ||
/// Path for library and path for file to read from | ||
struct Args { | ||
/// file path to read data from | ||
#[argh(positional, from_str_fn(read_path))] | ||
file_path: Option<PathBuf>, | ||
|
||
/// library path | ||
#[argh(option, short = 'l', default = "Path::new(\".\").into()")] | ||
pub lib_path: PathBuf, | ||
|
||
/// output file | ||
#[argh(option, short = 'o', default = "OutputFile::Stdout")] | ||
pub output: OutputFile, | ||
} | ||
|
||
fn read_path(path: &str) -> Result<PathBuf, String> { | ||
Ok(Path::new(path).into()) | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct ComponentCellsBackend; | ||
|
||
fn main() -> CalyxResult<()> { | ||
let p: Args = argh::from_env(); | ||
|
||
let ws = frontend::Workspace::construct(&p.file_path, &p.lib_path)?; | ||
|
||
let ctx: ir::Context = ir::from_ast::ast_to_ir(ws)?; | ||
|
||
let main_comp = ctx.entrypoint(); | ||
|
||
let mut component_info: HashSet<ComponentGroupInfo> = HashSet::new(); | ||
gen_component_info(&ctx, main_comp, &mut component_info); | ||
write_json(component_info.clone(), p.output)?; | ||
Ok(()) | ||
} | ||
|
||
fn id_serialize_passthrough<S>(id: &Id, ser: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: serde::Serializer, | ||
{ | ||
id.to_string().serialize(ser) | ||
} | ||
|
||
#[derive(PartialEq, Eq, Hash, Clone, Serialize)] | ||
struct ComponentGroupInfo { | ||
#[serde(serialize_with = "id_serialize_passthrough")] | ||
pub component: Id, | ||
pub groups: Vec<Id>, | ||
} | ||
|
||
/// Accumulates a set of components to the cells that they contain | ||
/// in the program with entrypoint `main_comp`. The contained cells | ||
/// are denoted with the name of the cell and the name of the component | ||
/// the cell is associated with. | ||
fn gen_component_info( | ||
ctx: &ir::Context, | ||
comp: &ir::Component, | ||
component_info: &mut HashSet<ComponentGroupInfo>, | ||
) { | ||
let mut curr_comp_info = ComponentGroupInfo { | ||
component: comp.name, | ||
groups: Vec::new(), | ||
}; | ||
for group_wrapped in comp.get_groups() { | ||
curr_comp_info.groups.push(group_wrapped.borrow().name()); | ||
} | ||
for cell in comp.cells.iter() { | ||
let cell_ref = cell.borrow(); | ||
if let ir::CellType::Component { name } = cell_ref.prototype { | ||
let component = ctx | ||
.components | ||
.iter() | ||
.find(|comp| comp.name == name) | ||
.unwrap(); | ||
gen_component_info(ctx, component, component_info); | ||
} | ||
} | ||
component_info.insert(curr_comp_info); | ||
} | ||
|
||
/// Write the collected set of component information to a JSON file. | ||
fn write_json( | ||
component_info: HashSet<ComponentGroupInfo>, | ||
file: OutputFile, | ||
) -> Result<(), io::Error> { | ||
let created_vec: Vec<ComponentGroupInfo> = | ||
component_info.into_iter().collect(); | ||
serde_json::to_writer_pretty(file.get_write(), &created_vec)?; | ||
Ok(()) | ||
} |
Oops, something went wrong.