Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion benchmarks/turnt_brilirs.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
command = "bril2json < {filename} | cargo run --manifest-path ../brilirs/Cargo.toml --quiet -- -p {args}"
command = "bril2json < {filename} | cargo +nightly run --manifest-path ../brilirs/Cargo.toml --quiet -- -p {args}"
output.out = "-"
output.prof = "2"
12 changes: 10 additions & 2 deletions brilirs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = "~2.31"
clap = "~2.33"
fxhash = "0.2"
mimalloc = "0.1"

[dependencies.bril-rs]
version = "0.1.0"
path = "../bril-rs"
features = ["ssa", "memory", "float", "speculate"]
features = ["ssa", "memory", "float", "speculate"]

[profile.release]
# this can shave off a few ms but doubles the build time so it's not really worth it
# codegen-units = 1
lto = true
panic = "abort"
13 changes: 12 additions & 1 deletion brilirs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@ test:
benchmark:
turnt -c turnt_brilirs.toml $(BENCHMARKS)

.PHONY: release
release:
RUSTFLAGS="-C target-cpu=native" cargo +nightly build --release

.PHONY: compare
compare: release
./benchmark.sh
#hyperfine --export-markdown results.md --warmup 5 \
-L interp brili,./target/release/brilirs \
"bril2json < ../benchmarks/check-primes.bril | {interp} -p 50"

# This is primarily used for running examples and debuging a bril program
.PHONY: example
example:
bril2json < ../benchmarks/mat-mul.bril | cargo run -- -p 50 109658
bril2json < ../benchmarks/sqrt.bril | cargo +nightly run
34 changes: 34 additions & 0 deletions brilirs/benchmark.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/bash

files=( "../benchmarks/ackermann.bril" "../benchmarks/binary-fmt.bril" "../benchmarks/check-primes.bril" \
"../benchmarks/collatz.bril" "../benchmarks/digital-root.bril" "../benchmarks/eight-queens.bril" \
"../benchmarks/euclid.bril" "../benchmarks/fib.bril" "../benchmarks/fizz-buzz.bril" \
"../benchmarks/gcd.bril" "../benchmarks/loopfact.bril" "../benchmarks/mat-mul.bril" \
"../benchmarks/orders.bril" "../benchmarks/perfect.bril" "../benchmarks/pythagorean_triple.bril" \
"../benchmarks/quadratic.bril" "../benchmarks/ray-sphere-intersection.bril" \
"../benchmarks/recfact.bril" "../benchmarks/sieve.bril" "../benchmarks/sqrt.bril" \
"../benchmarks/sum-bits.bril" "../benchmarks/sum-sq-diff.bril"
)
jsons=( "../benchmarks/ackermann.json" "../benchmarks/binary-fmt.json" "../benchmarks/check-primes.json" \
"../benchmarks/collatz.json" "../benchmarks/digital-root.json" "../benchmarks/eight-queens.json" \
"../benchmarks/euclid.json" "../benchmarks/fib.json" "../benchmarks/fizz-buzz.json" \
"../benchmarks/gcd.json" "../benchmarks/loopfact.json" "../benchmarks/mat-mul.json" \
"../benchmarks/orders.json" "../benchmarks/perfect.json" "../benchmarks/pythagorean_triple.json" \
"../benchmarks/quadratic.json" "../benchmarks/ray-sphere-intersection.json" \
"../benchmarks/recfact.json" "../benchmarks/sieve.json" "../benchmarks/sqrt.json" \
"../benchmarks/sum-bits.json" "../benchmarks/sum-sq-diff.json"
)
args=( "3 6" "128" "50" "7" "645634654" "8" "" "10" "101" "4 20" "8" "50 109658" "96 false" "496" "125" \
"-5 8 21" "" "8" "100" "" "42" "100")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a little bit too bad that this script needs to re-list all the benchmarks and all their arguments instead of just using ../benchmarks/*.bril. Maybe Brench could help with this—it could even wrap a call to Hyperfine? Not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't really tried to use Brench yet but I will look into it. I was mostly going off of the blog post so I threw something together.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The closest I've gotten is the following one-liner as a single stage pipeline for brench. (export bril=$(bril2json </dev/stdin); export arg='3 6'; hyperfine -s basic --warmup 5 'echo $bril | brili -p $arg') where I'm only trying to get the Ackermann.bril benchmark to run. I get a nice ValueError: I/O operation on closed file. when running Brench which I'm unsure of. It runs as expected on it's own(providing the ackermann file to stdin).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird. Any chance it's a bash/sh difference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#!/bin/sh
ps -p $$

(export bril=$(bril2json </dev/stdin); export arg='3 6'; hyperfine -s basic --warmup 5 'echo $bril | brili -p $arg') < ../benchmarks/ackermann.bril

This appears to show the above line using /bin/sh and running without issue.


for i in "${!files[@]}"; do
bril2json < ${files[i]} > ${jsons[i]}

export json=${jsons[i]}
export arg=${args[i]}
echo "file is ${files[i]}"
echo "arg is $arg"
hyperfine --warmup 5 -L interp brili,./target/release/brilirs '{interp} -p $arg < $json'

rm ${jsons[i]}
done
26 changes: 26 additions & 0 deletions brilirs/long_benchmark.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

# Some ideas for faster code
# Feature gate type checking?

files=( "../benchmarks/ackermann.bril" "../benchmarks/eight-queens.bril" \
"../benchmarks/mat-mul.bril"

)
jsons=( "../benchmarks/ackermann.json" "../benchmarks/eight-queens.json" \
"../benchmarks/mat-mul.json"

)
args=( "3 6" "8" "50 109658")

for i in "${!files[@]}"; do
bril2json < ${files[i]} > ${jsons[i]}

export json=${jsons[i]}
export arg=${args[i]}
echo "file is ${files[i]}"
echo "arg is $arg"
hyperfine --warmup 5 -L interp brili,./target/release/brilirs '{interp} -p $arg < $json'

rm ${jsons[i]}
done
138 changes: 115 additions & 23 deletions brilirs/src/basic_block.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
use bril_rs::{Function, Program};
use std::collections::HashMap;
use bril_rs::{Function, Instruction, Program};
use fxhash::FxHashMap;
use error::InterpError;

use crate::error;

// A program represented as basic blocks.
#[derive(Debug)]
pub struct BBProgram {
pub func_index: HashMap<String, BBFunction>,
pub func_index: FxHashMap<String, BBFunction>,
}

impl BBProgram {
pub fn new(prog: Program) -> BBProgram {
BBProgram {
pub fn new(prog: Program) -> Result<BBProgram, InterpError> {
let num_funcs = prog.functions.len();
let bb = BBProgram {
func_index: prog
.functions
.into_iter()
.map(|func| (func.name.clone(), BBFunction::new(func)))
.collect(),
};
if bb.func_index.len() != num_funcs {
Err(InterpError::DuplicateFunction)
} else {
Ok(bb)
}
}

Expand All @@ -26,7 +35,11 @@ impl BBProgram {
#[derive(Debug)]
pub struct BasicBlock {
pub label: Option<String>,
// These two vecs work in parallel
// One is the normal instruction
// The other contains the numified version of the destination and arguments
pub instrs: Vec<bril_rs::Instruction>,
pub numified_instrs: Vec<NumifiedInstruction>,
pub exit: Vec<usize>,
}

Expand All @@ -35,43 +48,108 @@ impl BasicBlock {
BasicBlock {
label: None,
instrs: Vec::new(),
numified_instrs: Vec::new(),
exit: Vec::new(),
}
}
}

#[derive(Debug)]
pub struct NumifiedInstruction {
pub dest: Option<u32>,
pub args: Vec<u32>,
}

fn get_num_from_map(
var: &str,
num_of_vars: &mut u32,
num_var_map: &mut FxHashMap<String, u32>,
) -> u32 {
match num_var_map.get(var) {
Some(i) => *i,
None => {
let x = *num_of_vars;
num_var_map.insert(var.to_string(), x);
*num_of_vars += 1;
x
}
}
}

impl NumifiedInstruction {
pub fn create(
instr: &Instruction,
num_of_vars: &mut u32,
num_var_map: &mut FxHashMap<String, u32>,
) -> Self {
match instr {
Instruction::Constant { dest, .. } => NumifiedInstruction {
dest: Some(get_num_from_map(dest, num_of_vars, num_var_map)),
args: Vec::new(),
},
Instruction::Value { dest, args, .. } => NumifiedInstruction {
dest: Some(get_num_from_map(dest, num_of_vars, num_var_map)),
args: args
.iter()
.map(|v| get_num_from_map(v, num_of_vars, num_var_map))
.collect(),
},
Instruction::Effect { args, .. } => NumifiedInstruction {
dest: None,
args: args
.iter()
.map(|v| get_num_from_map(v, num_of_vars, num_var_map))
.collect(),
},
}
}
}

#[derive(Debug)]
pub struct BBFunction {
pub name: String,
pub args: Vec<bril_rs::Argument>,
pub return_type: Option<bril_rs::Type>,
pub blocks: Vec<BasicBlock>,
pub label_map: HashMap<String, usize>,
// the following is an optimization by replacing the string representation of variables with a number
// Variable names are ordered from 0 to num_of_vars.
// These replacements are found for function args and for code in the BasicBlocks
pub num_of_vars: u32,
pub args_as_nums: Vec<u32>,
}

impl BBFunction {
pub fn new(f: Function) -> BBFunction {
let mut func = BBFunction::find_basic_blocks(f);
func.build_cfg();
let (mut func, label_map) = BBFunction::find_basic_blocks(f);
func.build_cfg(label_map);
func
}

fn find_basic_blocks(func: bril_rs::Function) -> BBFunction {
fn find_basic_blocks(func: bril_rs::Function) -> (BBFunction, FxHashMap<String, usize>) {
let mut blocks = Vec::new();
let mut label_map = HashMap::new();
let mut label_map = FxHashMap::default();

let mut num_of_vars = 0;
let mut num_var_map = FxHashMap::default();

let args_as_nums = func
.args
.iter()
.map(|a| get_num_from_map(&a.name, &mut num_of_vars, &mut num_var_map))
.collect();

let mut curr_block = BasicBlock::new();
for instr in func.instrs.into_iter() {
match instr {
bril_rs::Code::Label { ref label } => {
bril_rs::Code::Label { label } => {
if !curr_block.instrs.is_empty() || curr_block.label.is_some() {
if let Some(old_label) = curr_block.label.as_ref() {
label_map.insert(old_label.to_string(), blocks.len());
}
blocks.push(curr_block);
curr_block = BasicBlock::new();
}
curr_block.label = Some(label.clone());
curr_block.label = Some(label);
}
bril_rs::Code::Instruction(bril_rs::Instruction::Effect {
op,
Expand All @@ -82,19 +160,30 @@ impl BBFunction {
|| op == bril_rs::EffectOps::Branch
|| op == bril_rs::EffectOps::Return =>
{
curr_block.instrs.push(bril_rs::Instruction::Effect {
let i = bril_rs::Instruction::Effect {
op,
args,
funcs,
labels,
});
};
curr_block.numified_instrs.push(NumifiedInstruction::create(
&i,
&mut num_of_vars,
&mut num_var_map,
));
curr_block.instrs.push(i);
if let Some(l) = curr_block.label.as_ref() {
label_map.insert(l.to_string(), blocks.len());
}
blocks.push(curr_block);
curr_block = BasicBlock::new();
}
bril_rs::Code::Instruction(code) => {
curr_block.numified_instrs.push(NumifiedInstruction::create(
&code,
&mut num_of_vars,
&mut num_var_map,
));
curr_block.instrs.push(code);
}
}
Expand All @@ -107,16 +196,20 @@ impl BBFunction {
blocks.push(curr_block);
}

BBFunction {
name: func.name,
args: func.args,
return_type: func.return_type,
blocks,
(
BBFunction {
name: func.name,
args: func.args,
return_type: func.return_type,
blocks,
args_as_nums,
num_of_vars,
},
label_map,
}
)
}

fn build_cfg(&mut self) {
fn build_cfg(&mut self, label_map: FxHashMap<String, usize>) {
let last_idx = self.blocks.len() - 1;
for (i, block) in self.blocks.iter_mut().enumerate() {
// If we're before the last block
Expand All @@ -136,8 +229,7 @@ impl BBFunction {
{
for l in labels {
block.exit.push(
*self
.label_map
*label_map
.get(&l)
.unwrap_or_else(|| panic!("No label {} found.", &l)),
);
Expand Down
Loading