Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Profiling feature #6565

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 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
869 changes: 454 additions & 415 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions forc-pkg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ regex = "^1.10.2"

[target.'cfg(not(target_os = "macos"))'.dependencies]
tritao marked this conversation as resolved.
Show resolved Hide resolved
sysinfo = "0.29"

5 changes: 5 additions & 0 deletions forc-pkg/src/manifest/build_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub struct BuildProfile {
#[serde(default)]
pub time_phases: bool,
#[serde(default)]
pub profile: bool,
#[serde(default)]
pub metrics_outfile: Option<String>,
#[serde(default)]
pub include_tests: bool,
Expand Down Expand Up @@ -60,6 +62,7 @@ impl BuildProfile {
print_bytecode_spans: false,
terse: false,
time_phases: false,
profile: false,
metrics_outfile: None,
include_tests: false,
error_on_warnings: false,
Expand All @@ -83,6 +86,7 @@ impl BuildProfile {
print_bytecode_spans: false,
terse: false,
time_phases: false,
profile: false,
metrics_outfile: None,
include_tests: false,
error_on_warnings: false,
Expand Down Expand Up @@ -155,6 +159,7 @@ mod tests {
print_bytecode_spans: false,
terse: true,
time_phases: true,
profile: true,
metrics_outfile: Some("metrics_outfile".into()),
include_tests: true,
error_on_warnings: true,
Expand Down
69 changes: 67 additions & 2 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ pub struct BuildOpts {
pub release: bool,
/// Output the time elapsed over each part of the compilation process.
pub time_phases: bool,
/// Profile the build process.
pub profile: bool,
/// If set, outputs compilation metrics info in JSON format.
pub metrics_outfile: Option<String>,
/// Warnings must be treated as compiler errors.
Expand Down Expand Up @@ -1564,6 +1566,7 @@ pub fn sway_build_config(
.with_print_ir(build_profile.print_ir.clone())
.with_include_tests(build_profile.include_tests)
.with_time_phases(build_profile.time_phases)
.with_profile(build_profile.profile)
.with_metrics(build_profile.metrics_outfile.clone())
.with_optimization_level(build_profile.optimization_level)
.with_experimental(sway_core::ExperimentalFlags {
Expand Down Expand Up @@ -1785,6 +1788,7 @@ pub fn compile(

// First, compile to an AST. We'll update the namespace and check for JSON ABI output.
let ast_res = time_expr!(
pkg.name,
"compile to ast",
"compile_to_ast",
sway_core::compile_to_ast(
Expand Down Expand Up @@ -1823,6 +1827,7 @@ pub fn compile(
}

let asm_res = time_expr!(
pkg.name,
"compile ast to asm",
"compile_ast_to_asm",
sway_core::ast_to_asm(&handler, engines, &programs, &sway_build_config),
Expand All @@ -1837,6 +1842,7 @@ pub fn compile(
let mut program_abi = match pkg.target {
BuildTarget::Fuel => {
let program_abi_res = time_expr!(
pkg.name,
"generate JSON ABI program",
"generate_json_abi",
fuel_abi::generate_program_abi(
Expand Down Expand Up @@ -1875,6 +1881,7 @@ pub fn compile(
};

let abi = time_expr!(
pkg.name,
"generate JSON ABI program",
"generate_json_abi",
evm_abi::generate_abi_program(typed_program, engines),
Expand All @@ -1899,15 +1906,16 @@ pub fn compile(
.map(|finalized_entry| PkgEntry::from_finalized_entry(finalized_entry, engines))
.collect::<anyhow::Result<_>>()?;

let asm = match asm_res {
let mut asm = match asm_res {
Err(_) => return fail(handler),
Ok(asm) => asm,
};

let bc_res = time_expr!(
pkg.name,
"compile asm to bytecode",
"compile_asm_to_bytecode",
sway_core::asm_to_bytecode(&handler, asm, source_map, engines.se(), &sway_build_config),
sway_core::asm_to_bytecode(&handler, &mut asm, source_map, engines.se(), &sway_build_config),
Some(sway_build_config.clone()),
metrics
);
Expand Down Expand Up @@ -1957,9 +1965,64 @@ pub fn compile(
warnings,
metrics,
};
if sway_build_config.profile {
report_assembly_information(&asm, &compiled_package);
}

Ok(compiled_package)
}

/// Reports assembly information for a compiled package to an external `dyno` process through `stdout`.
fn report_assembly_information(
compiled_asm: &sway_core::CompiledAsm,
compiled_package: &CompiledPackage,
) {
// Get the bytes of the compiled package.
let mut bytes = compiled_package.bytecode.bytes.clone();

// Attempt to get the data section offset out of the compiled package bytes.
let data_offset = u64::from_be_bytes(bytes.iter().skip(8).take(8).cloned().collect::<Vec<_>>().try_into().unwrap());
let data_section_size = bytes.len() as u64 - data_offset;

// Remove the data section from the compiled package bytes.
bytes.truncate(data_offset as usize);

// Calculate the unpadded size of each data section section.
// Implementation based directly on `sway_core::asm_generation::Entry::to_bytes`, referenced here:
// https://github.com/FuelLabs/sway/blob/afd6a6709e7cb11c676059a5004012cc466e653b/sway-core/src/asm_generation/fuel/data_section.rs#L147
fn calculate_entry_size(entry: &sway_core::asm_generation::Entry) -> u64 {
match &entry.value {
sway_core::asm_generation::Datum::Byte(value) => std::mem::size_of_val(value) as u64,

sway_core::asm_generation::Datum::Word(value) => std::mem::size_of_val(value) as u64,

sway_core::asm_generation::Datum::ByteArray(bytes)
| sway_core::asm_generation::Datum::Slice(bytes) => {
if bytes.len() % 8 == 0 {
bytes.len() as u64
} else {
((bytes.len() + 7) & 0xfffffff8_usize) as u64
}
}

sway_core::asm_generation::Datum::Collection(items) => items.iter().map(calculate_entry_size).sum(),
}
}

// Compute the assembly information to be reported.
let asm_information = sway_core::asm_generation::AsmInformation {
bytecode_size: bytes.len() as _,
data_section: sway_core::asm_generation::DataSectionInformation {
size: data_section_size,
used: compiled_asm.0.data_section.value_pairs.iter().map(calculate_entry_size).sum(),
value_pairs: compiled_asm.0.data_section.value_pairs.clone(),
}
};

// Report the assembly information to the `dyno` process through `stdout`.
println!("/dyno info {}", serde_json::to_string(&asm_information).unwrap());
}

impl PkgEntry {
/// Returns whether this `PkgEntry` corresponds to a test.
pub fn is_test(&self) -> bool {
Expand Down Expand Up @@ -2062,6 +2125,7 @@ fn build_profile_from_opts(
pkg,
print,
time_phases,
profile: profile_opt,
build_profile,
release,
metrics_outfile,
Expand Down Expand Up @@ -2103,6 +2167,7 @@ fn build_profile_from_opts(
profile.print_bytecode_spans |= print.bytecode_spans;
profile.terse |= pkg.terse;
profile.time_phases |= time_phases;
profile.profile |= profile_opt;
if profile.metrics_outfile.is_none() {
profile.metrics_outfile.clone_from(metrics_outfile);
}
Expand Down
1 change: 1 addition & 0 deletions forc-plugins/forc-client/src/op/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ fn build_opts_from_cmd(cmd: &cmd::Deploy) -> pkg::BuildOpts {
reverse_order: cmd.print.reverse_order,
},
time_phases: cmd.print.time_phases,
profile: cmd.print.profile,
metrics_outfile: cmd.print.metrics_outfile.clone(),
minify: pkg::MinifyOpts {
json_abi: cmd.minify.json_abi,
Expand Down
1 change: 1 addition & 0 deletions forc-plugins/forc-client/src/op/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ fn build_opts_from_cmd(cmd: &cmd::Run) -> pkg::BuildOpts {
release: cmd.build_profile.release,
error_on_warnings: cmd.build_profile.error_on_warnings,
time_phases: cmd.print.time_phases,
profile: cmd.print.profile,
metrics_outfile: cmd.print.metrics_outfile.clone(),
binary_outfile: cmd.build_output.bin_file.clone(),
debug_outfile: cmd.build_output.debug_file.clone(),
Expand Down
4 changes: 4 additions & 0 deletions forc-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ pub struct TestOpts {
pub error_on_warnings: bool,
/// Output the time elapsed over each part of the compilation process.
pub time_phases: bool,
/// Profile the compilation process.
pub profile: bool,
/// Output compilation metrics into file.
pub metrics_outfile: Option<String>,
/// Set of experimental flags
Expand Down Expand Up @@ -452,6 +454,7 @@ impl From<TestOpts> for pkg::BuildOpts {
release: val.release,
error_on_warnings: val.error_on_warnings,
time_phases: val.time_phases,
profile: val.profile,
metrics_outfile: val.metrics_outfile,
tests: true,
member_filter: Default::default(),
Expand All @@ -474,6 +477,7 @@ impl TestOpts {
release: self.release,
error_on_warnings: self.error_on_warnings,
time_phases: self.time_phases,
profile: self.profile,
metrics_outfile: self.metrics_outfile,
tests: true,
member_filter: Default::default(),
Expand Down
1 change: 1 addition & 0 deletions forc/src/cli/commands/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ fn opts_from_cmd(cmd: Command) -> forc_test::TestOpts {
reverse_order: cmd.build.print.reverse_order,
},
time_phases: cmd.build.print.time_phases,
profile: cmd.build.print.profile,
metrics_outfile: cmd.build.print.metrics_outfile,
minify: pkg::MinifyOpts {
json_abi: cmd.build.minify.json_abi,
Expand Down
3 changes: 3 additions & 0 deletions forc/src/cli/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ pub struct Print {
/// Output the time elapsed over each part of the compilation process.
#[clap(long)]
pub time_phases: bool,
/// Profile the compilation process.
#[clap(long)]
pub profile: bool,
/// Output build errors and warnings in reverse order.
#[clap(long)]
pub reverse_order: bool,
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fn opts_from_cmd(cmd: BuildCommand) -> pkg::BuildOpts {
reverse_order: cmd.build.print.reverse_order,
},
time_phases: cmd.build.print.time_phases,
profile: cmd.build.print.profile,
metrics_outfile: cmd.build.print.metrics_outfile,
minify: pkg::MinifyOpts {
json_abi: cmd.build.minify.json_abi,
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_contract_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ fn build_opts_from_cmd(cmd: &ContractIdCommand) -> pkg::BuildOpts {
reverse_order: cmd.print.reverse_order,
},
time_phases: cmd.print.time_phases,
profile: cmd.print.profile,
metrics_outfile: cmd.print.metrics_outfile.clone(),
minify: pkg::MinifyOpts {
json_abi: cmd.minify.json_abi,
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_predicate_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn build_opts_from_cmd(cmd: PredicateRootCommand) -> pkg::BuildOpts {
reverse_order: cmd.print.reverse_order,
},
time_phases: cmd.print.time_phases,
profile: cmd.print.profile,
metrics_outfile: cmd.print.metrics_outfile,
minify: pkg::MinifyOpts {
json_abi: cmd.minify.json_abi,
Expand Down
18 changes: 18 additions & 0 deletions sway-core/src/asm_generation/finalized_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ use sway_types::SourceEngine;
use either::Either;
use std::{collections::BTreeMap, fmt};

/// Represents an ASM set which has had register allocation, jump elimination, and optimization
/// applied to it
#[derive(Clone, serde::Serialize)]
pub struct AsmInformation {
pub bytecode_size: u64,
pub data_section: DataSectionInformation,
}

#[derive(Default, Clone, Debug, serde::Serialize)]
pub struct DataSectionInformation {
/// The total size of the data section in bytes
pub size : u64,
/// The used size of the data section in bytes
pub used: u64,
/// The data to be put in the data section of the asm
pub value_pairs: Vec<Entry>,
}

/// Represents an ASM set which has had register allocation, jump elimination, and optimization
/// applied to it
#[derive(Clone)]
Expand Down
4 changes: 2 additions & 2 deletions sway-core/src/asm_generation/fuel/data_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{fmt, iter::repeat};

// An entry in the data section. It's important for the size to be correct, especially for unions
// where the size could be larger than the represented value.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, serde::Serialize)]
pub struct Entry {
pub value: Datum,
pub padding: Padding,
Expand All @@ -13,7 +13,7 @@ pub struct Entry {
pub name: Option<String>,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, serde::Serialize)]
pub enum Datum {
Byte(u8),
Word(u64),
Expand Down
3 changes: 2 additions & 1 deletion sway-core/src/asm_generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ pub mod fuel;
pub mod instruction_set;

mod finalized_asm;
pub use finalized_asm::{CompiledBytecode, FinalizedAsm, FinalizedEntry};
pub use finalized_asm::*;
pub use fuel::data_section::{Datum, Entry};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ProgramKind {
Expand Down
9 changes: 9 additions & 0 deletions sway-core/src/build_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ pub struct BuildConfig {
pub(crate) include_tests: bool,
pub(crate) optimization_level: OptLevel,
pub time_phases: bool,
pub profile: bool,
pub metrics_outfile: Option<String>,
pub experimental: ExperimentalFlags,
pub lsp_mode: Option<LspConfig>,
Expand Down Expand Up @@ -239,6 +240,7 @@ impl BuildConfig {
print_ir: PrintIr::default(),
include_tests: false,
time_phases: false,
profile: false,
metrics_outfile: None,
optimization_level: OptLevel::Opt0,
experimental: ExperimentalFlags {
Expand Down Expand Up @@ -288,6 +290,13 @@ impl BuildConfig {
}
}

pub fn with_profile(self, a: bool) -> Self {
Self {
profile: a,
..self
}
}

pub fn with_metrics(self, a: Option<String>) -> Self {
Self {
metrics_outfile: a,
Expand Down
8 changes: 5 additions & 3 deletions sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ pub fn compile_to_ast(

// Parse the program to a concrete syntax tree (CST).
let parse_program_opt = time_expr!(
package_name,
"parse the program to a concrete syntax tree (CST)",
"parse_cst",
parse(input, handler, engines, build_config),
Expand All @@ -775,6 +776,7 @@ pub fn compile_to_ast(

// Type check (+ other static analysis) the CST to a typed AST.
let typed_res = time_expr!(
package_name,
"parse the concrete syntax tree (CST) to a typed AST",
"parse_ast",
parsed_to_ast(
Expand Down Expand Up @@ -983,21 +985,21 @@ pub fn compile_to_bytecode(
source_map: &mut SourceMap,
package_name: &str,
) -> Result<CompiledBytecode, ErrorEmitted> {
let asm_res = compile_to_asm(
let mut asm_res = compile_to_asm(
handler,
engines,
input,
initial_namespace,
build_config,
package_name,
)?;
asm_to_bytecode(handler, asm_res, source_map, engines.se(), build_config)
asm_to_bytecode(handler, &mut asm_res, source_map, engines.se(), build_config)
}

/// Given the assembly (opcodes), compile to [CompiledBytecode], containing the asm in bytecode form.
pub fn asm_to_bytecode(
handler: &Handler,
mut asm: CompiledAsm,
asm: &mut CompiledAsm,
source_map: &mut SourceMap,
source_engine: &SourceEngine,
build_config: &BuildConfig,
Expand Down
Loading