use crate::prelude::*;
use num_traits::FromPrimitive;
use std::sync::atomic::AtomicU64;
use std::time::Duration;
#[derive(Default)]
pub struct DBPerf<T> {
chip: T,
perf_counters: PerfCounters,
}
struct PerfCounters {
perf_counters: Vec<(FnName, PerfCounter)>,
}
impl Default for PerfCounters {
fn default() -> Self {
Self::new()
}
}
impl<T> DBPerf<T> {
pub fn new(chip: T) -> Self {
Self {
chip,
perf_counters: PerfCounters::new(),
}
}
pub fn get_stats_all(&self) -> Vec<(FnName, PerfCounterResult)> {
self.perf_counters
.perf_counters
.iter()
.map(|(fname, counter)| (*fname, counter.atomic_read()))
.collect()
}
pub fn get_stats(&self, function_name: FnName) -> PerfCounterResult {
self.perf_counters.get(function_name).atomic_read()
}
pub fn into_inner(self) -> T {
self.chip
}
}
impl PerfCounters {
fn new() -> Self {
Self {
perf_counters: (0..)
.map(FnName::from_usize)
.take_while(|fname| fname.is_some())
.map(|fname| (fname.unwrap(), Default::default()))
.collect(),
}
}
fn get(&self, function_name: FnName) -> &PerfCounter {
&self.perf_counters[function_name as usize].1
}
}
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive, FromPrimitive, Debug)]
#[allow(non_camel_case_types, missing_docs)]
pub enum FnName {
cell_by_name = 0,
cell_instance_by_name,
cell_name,
cell_instance_name,
parent_cell,
template_cell,
for_each_cell,
each_cell_vec,
each_cell,
for_each_cell_instance,
each_cell_instance_vec,
each_cell_instance,
for_each_cell_dependency,
each_cell_dependency_vec,
each_cell_dependency,
num_cell_dependencies,
for_each_dependent_cell,
each_dependent_cell_vec,
each_dependent_cell,
num_dependent_cells,
for_each_cell_reference,
each_cell_reference_vec,
each_cell_reference,
num_cell_references,
num_child_instances,
num_cells,
get_chip_property,
get_cell_property,
get_cell_instance_property,
create_cell,
remove_cell,
create_cell_instance,
remove_cell_instance,
rename_cell_instance,
rename_cell,
set_chip_property,
set_cell_property,
set_cell_instance_property,
insert_shape,
set_dbu,
create_layer,
create_layer_with_id,
set_layer_name,
remove_shape,
replace_shape,
set_transform,
set_shape_property,
create_pin,
remove_pin,
rename_pin,
create_net,
rename_net,
remove_net,
connect_pin,
connect_pin_instance,
disconnect_pin,
disconnect_pin_instance,
shapes_of_net,
shapes_of_pin,
get_net_of_shape,
get_pin_of_shape,
set_pin_of_shape,
set_net_of_shape,
}
impl std::fmt::Display for FnName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug)]
pub struct PerfCounter {
num_calls: AtomicU64,
total_time_ns: AtomicU64,
min_time_ns: AtomicU64,
max_time_ns: AtomicU64,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PerfCounterResult {
pub num_calls: u64,
pub total_time: Duration,
pub min_time: Duration,
pub max_time: Duration,
}
impl PerfCounterResult {
pub fn avg_time(&self) -> Option<Duration> {
(self.num_calls > 0).then(|| self.total_time / (self.num_calls as u32))
}
}
impl PerfCounter {
pub fn atomic_read(&self) -> PerfCounterResult {
use std::sync::atomic::Ordering::Relaxed;
loop {
let read = || PerfCounterResult {
num_calls: self.num_calls.load(Relaxed),
total_time: Duration::from_nanos(self.total_time_ns.load(Relaxed)),
min_time: Duration::from_nanos(self.min_time_ns.load(Relaxed)),
max_time: Duration::from_nanos(self.max_time_ns.load(Relaxed)),
};
let r = read();
if r == read() {
break r;
}
std::hint::spin_loop();
}
}
}
impl Default for PerfCounter {
fn default() -> Self {
Self {
num_calls: Default::default(),
total_time_ns: Default::default(),
min_time_ns: AtomicU64::new(u64::MAX),
max_time_ns: Default::default(),
}
}
}
pub struct PerfCounterManager<'a> {
start_time: std::time::Instant,
counter: &'a PerfCounter,
}
impl<'a> PerfCounterManager<'a> {
fn stop_measurement(self) {
let elapsed = self.start_time.elapsed();
let elapsed_ns = elapsed.as_nanos() as u64;
use std::sync::atomic::Ordering::*;
self.counter.total_time_ns.fetch_add(elapsed_ns, Relaxed);
self.counter.min_time_ns.fetch_min(elapsed_ns, Relaxed);
self.counter.max_time_ns.fetch_max(elapsed_ns, Relaxed);
self.counter.num_calls.fetch_add(1, Relaxed);
}
}
impl PerfCounter {
#[must_use]
fn start_measurement(&self) -> PerfCounterManager {
PerfCounterManager {
start_time: std::time::Instant::now(),
counter: self,
}
}
fn measure<R>(&self, f: impl FnOnce() -> R) -> R {
let m = self.start_measurement();
let r = f();
m.stop_measurement();
r
}
}
#[portrait::fill(portrait::delegate(H))]
impl<H: HierarchyIds> HierarchyIds for DBPerf<H> {}
#[portrait::fill(portrait::delegate(N))]
impl<N: NetlistIds> NetlistIds for DBPerf<N> {}
impl<H: HierarchyBase> HierarchyBase for DBPerf<H> {
type NameType = H::NameType;
fn cell_by_name(&self, name: &str) -> Option<H::CellId> {
self.perf_counters
.get(FnName::cell_by_name)
.measure(|| self.chip.cell_by_name(name))
}
fn cell_instance_by_name(&self, parent_cell: &H::CellId, name: &str) -> Option<H::CellInstId> {
self.perf_counters
.get(FnName::cell_instance_by_name)
.measure(|| self.chip.cell_instance_by_name(parent_cell, name))
}
fn cell_name(&self, cell: &H::CellId) -> H::NameType {
self.perf_counters
.get(FnName::cell_name)
.measure(|| self.chip.cell_name(cell))
}
fn cell_instance_name(&self, cell_inst: &H::CellInstId) -> Option<H::NameType> {
self.perf_counters
.get(FnName::cell_instance_name)
.measure(|| self.chip.cell_instance_name(cell_inst))
}
fn parent_cell(&self, cell_instance: &H::CellInstId) -> H::CellId {
self.perf_counters
.get(FnName::parent_cell)
.measure(|| self.chip.parent_cell(cell_instance))
}
fn template_cell(&self, cell_instance: &H::CellInstId) -> H::CellId {
self.perf_counters
.get(FnName::template_cell)
.measure(|| self.chip.template_cell(cell_instance))
}
fn for_each_cell<F>(&self, f: F)
where
F: FnMut(H::CellId),
{
self.perf_counters
.get(FnName::for_each_cell)
.measure(|| self.chip.for_each_cell(f))
}
fn each_cell_vec(&self) -> Vec<H::CellId> {
self.perf_counters
.get(FnName::each_cell_vec)
.measure(|| self.chip.each_cell_vec())
}
fn each_cell(&self) -> Box<dyn Iterator<Item = H::CellId> + '_> {
self.perf_counters
.get(FnName::each_cell)
.measure(|| self.chip.each_cell())
}
fn for_each_cell_instance<F>(&self, cell: &H::CellId, f: F)
where
F: FnMut(H::CellInstId),
{
self.perf_counters
.get(FnName::each_cell_instance)
.measure(|| self.chip.for_each_cell_instance(cell, f))
}
fn each_cell_instance_vec(&self, cell: &H::CellId) -> Vec<H::CellInstId> {
self.perf_counters
.get(FnName::each_cell_instance_vec)
.measure(|| self.chip.each_cell_instance_vec(cell))
}
fn each_cell_instance(&self, cell: &H::CellId) -> Box<dyn Iterator<Item = H::CellInstId> + '_> {
self.perf_counters
.get(FnName::each_cell_instance)
.measure(|| self.chip.each_cell_instance(cell))
}
fn for_each_cell_dependency<F>(&self, cell: &H::CellId, f: F)
where
F: FnMut(H::CellId),
{
self.perf_counters
.get(FnName::for_each_cell_dependency)
.measure(|| self.chip.for_each_cell_dependency(cell, f))
}
fn each_cell_dependency_vec(&self, cell: &H::CellId) -> Vec<H::CellId> {
self.perf_counters
.get(FnName::each_cell_dependency_vec)
.measure(|| self.chip.each_cell_dependency_vec(cell))
}
fn each_cell_dependency(&self, cell: &H::CellId) -> Box<dyn Iterator<Item = H::CellId> + '_> {
self.perf_counters
.get(FnName::each_cell_dependency)
.measure(|| self.chip.each_cell_dependency(cell))
}
fn num_cell_dependencies(&self, cell: &H::CellId) -> usize {
self.perf_counters
.get(FnName::num_cell_dependencies)
.measure(|| self.chip.num_cell_dependencies(cell))
}
fn for_each_dependent_cell<F>(&self, cell: &H::CellId, f: F)
where
F: FnMut(H::CellId),
{
self.perf_counters
.get(FnName::for_each_dependent_cell)
.measure(|| self.chip.for_each_dependent_cell(cell, f))
}
fn each_dependent_cell_vec(&self, cell: &H::CellId) -> Vec<H::CellId> {
self.perf_counters
.get(FnName::each_dependent_cell_vec)
.measure(|| self.chip.each_dependent_cell_vec(cell))
}
fn each_dependent_cell(&self, cell: &H::CellId) -> Box<dyn Iterator<Item = H::CellId> + '_> {
self.perf_counters
.get(FnName::each_dependent_cell)
.measure(|| self.chip.each_dependent_cell(cell))
}
fn num_dependent_cells(&self, cell: &H::CellId) -> usize {
self.perf_counters
.get(FnName::num_dependent_cells)
.measure(|| self.chip.num_dependent_cells(cell))
}
fn for_each_cell_reference<F>(&self, cell: &H::CellId, f: F)
where
F: FnMut(H::CellInstId),
{
self.perf_counters
.get(FnName::for_each_cell_reference)
.measure(|| self.chip.for_each_cell_reference(cell, f))
}
fn each_cell_reference_vec(&self, cell: &H::CellId) -> Vec<H::CellInstId> {
self.perf_counters
.get(FnName::each_cell_reference_vec)
.measure(|| self.chip.each_cell_reference_vec(cell))
}
fn each_cell_reference(
&self,
cell: &H::CellId,
) -> Box<dyn Iterator<Item = H::CellInstId> + '_> {
self.perf_counters
.get(FnName::each_cell_reference)
.measure(|| self.chip.each_cell_reference(cell))
}
fn num_cell_references(&self, cell: &H::CellId) -> usize {
self.perf_counters
.get(FnName::num_cell_references)
.measure(|| self.chip.num_cell_references(cell))
}
fn num_child_instances(&self, cell: &H::CellId) -> usize {
self.perf_counters
.get(FnName::num_child_instances)
.measure(|| self.chip.num_child_instances(cell))
}
fn num_cells(&self) -> usize {
self.perf_counters
.get(FnName::num_cells)
.measure(|| self.chip.num_cells())
}
fn get_chip_property(&self, key: &H::NameType) -> Option<PropertyValue> {
self.perf_counters
.get(FnName::get_chip_property)
.measure(|| self.chip.get_chip_property(key))
}
fn get_cell_property(&self, cell: &H::CellId, key: &H::NameType) -> Option<PropertyValue> {
self.perf_counters
.get(FnName::get_cell_property)
.measure(|| self.chip.get_cell_property(cell, key))
}
fn get_cell_instance_property(
&self,
inst: &H::CellInstId,
key: &H::NameType,
) -> Option<PropertyValue> {
self.perf_counters
.get(FnName::get_cell_instance_property)
.measure(|| self.chip.get_cell_instance_property(inst, key))
}
}
#[portrait::fill(portrait::delegate(L))]
impl<L: LayoutIds> LayoutIds for DBPerf<L> {}
#[portrait::fill(portrait::delegate(L; self.chip))]
impl<L: LayoutBase> LayoutBase for DBPerf<L> {}
#[portrait::fill(portrait::delegate(N; self.chip))]
impl<N: NetlistBase> NetlistBase for DBPerf<N> {}
impl<H: HierarchyEdit> HierarchyEdit for DBPerf<H> {
fn create_cell(&mut self, name: H::NameType) -> H::CellId {
self.perf_counters
.get(FnName::create_cell)
.measure(|| self.chip.create_cell(name))
}
fn remove_cell(&mut self, cell_id: &H::CellId) {
self.perf_counters
.get(FnName::remove_cell)
.measure(|| self.chip.remove_cell(cell_id))
}
fn create_cell_instance(
&mut self,
parent_cell: &H::CellId,
template_cell: &H::CellId,
name: Option<H::NameType>,
) -> H::CellInstId {
self.perf_counters
.get(FnName::create_cell_instance)
.measure(|| {
self.chip
.create_cell_instance(parent_cell, template_cell, name)
})
}
fn remove_cell_instance(&mut self, inst: &H::CellInstId) {
self.perf_counters
.get(FnName::remove_cell_instance)
.measure(|| self.chip.remove_cell_instance(inst))
}
fn rename_cell_instance(&mut self, inst: &H::CellInstId, new_name: Option<H::NameType>) {
self.perf_counters
.get(FnName::rename_cell_instance)
.measure(|| self.chip.rename_cell_instance(inst, new_name))
}
fn rename_cell(&mut self, cell: &H::CellId, new_name: H::NameType) {
self.perf_counters
.get(FnName::rename_cell)
.measure(|| self.chip.rename_cell(cell, new_name))
}
fn set_chip_property(&mut self, key: H::NameType, value: PropertyValue) {
self.perf_counters
.get(FnName::set_chip_property)
.measure(|| self.chip.set_chip_property(key, value))
}
fn set_cell_property(&mut self, cell: &H::CellId, key: H::NameType, value: PropertyValue) {
self.perf_counters
.get(FnName::set_cell_property)
.measure(|| self.chip.set_cell_property(cell, key, value))
}
fn set_cell_instance_property(
&mut self,
inst: &H::CellInstId,
key: H::NameType,
value: PropertyValue,
) {
self.perf_counters
.get(FnName::set_cell_instance_property)
.measure(|| self.chip.set_cell_instance_property(inst, key, value))
}
}
impl<L: LayoutEdit> LayoutEdit for DBPerf<L> {
fn insert_shape(
&mut self,
parent_cell: &L::CellId,
layer: &L::LayerId,
geometry: Geometry<L::Coord>,
) -> L::ShapeId {
self.perf_counters
.get(FnName::insert_shape)
.measure(|| self.chip.insert_shape(parent_cell, layer, geometry))
}
fn set_dbu(&mut self, dbu: L::Coord) {
self.perf_counters
.get(FnName::set_dbu)
.measure(|| self.chip.set_dbu(dbu))
}
fn create_layer(&mut self, index: UInt, datatype: UInt) -> L::LayerId {
self.perf_counters
.get(FnName::create_layer)
.measure(|| self.chip.create_layer(index, datatype))
}
fn create_layer_with_id(
&mut self,
layer_id: L::LayerId,
index: UInt,
datatype: UInt,
) -> Result<(), ()> {
self.perf_counters
.get(FnName::create_layer_with_id)
.measure(|| self.chip.create_layer_with_id(layer_id, index, datatype))
}
fn set_layer_name(
&mut self,
layer: &L::LayerId,
name: Option<L::NameType>,
) -> Option<L::NameType> {
self.perf_counters
.get(FnName::set_layer_name)
.measure(|| self.chip.set_layer_name(layer, name))
}
fn remove_shape(&mut self, shape_id: &L::ShapeId) -> Option<Geometry<L::Coord>> {
self.perf_counters
.get(FnName::remove_shape)
.measure(|| self.chip.remove_shape(shape_id))
}
fn replace_shape(
&mut self,
shape_id: &L::ShapeId,
geometry: Geometry<L::Coord>,
) -> Geometry<L::Coord> {
self.perf_counters
.get(FnName::replace_shape)
.measure(|| self.chip.replace_shape(shape_id, geometry))
}
fn set_transform(&mut self, cell_inst: &L::CellInstId, tf: SimpleTransform<L::Coord>) {
self.perf_counters
.get(FnName::set_transform)
.measure(|| self.chip.set_transform(cell_inst, tf))
}
fn set_shape_property(&mut self, shape: &L::ShapeId, key: L::NameType, value: PropertyValue) {
self.perf_counters
.get(FnName::set_shape_property)
.measure(|| self.chip.set_shape_property(shape, key, value))
}
}
impl<N: NetlistEdit> NetlistEdit for DBPerf<N> {
fn create_pin(
&mut self,
cell: &Self::CellId,
name: Self::NameType,
direction: Direction,
) -> Self::PinId {
self.perf_counters
.get(FnName::create_pin)
.measure(|| self.chip.create_pin(cell, name, direction))
}
fn remove_pin(&mut self, id: &Self::PinId) {
self.perf_counters
.get(FnName::remove_pin)
.measure(|| self.chip.remove_pin(id))
}
fn rename_pin(&mut self, pin: &Self::PinId, new_name: Self::NameType) -> Self::NameType {
self.perf_counters
.get(FnName::rename_pin)
.measure(|| self.chip.rename_pin(pin, new_name))
}
fn create_net(&mut self, parent: &Self::CellId, name: Option<Self::NameType>) -> Self::NetId {
self.perf_counters
.get(FnName::create_net)
.measure(|| self.chip.create_net(parent, name))
}
fn rename_net(
&mut self,
net_id: &Self::NetId,
new_name: Option<Self::NameType>,
) -> Option<Self::NameType> {
self.perf_counters
.get(FnName::rename_net)
.measure(|| self.chip.rename_net(net_id, new_name))
}
fn remove_net(&mut self, net: &Self::NetId) {
self.perf_counters
.get(FnName::remove_net)
.measure(|| self.chip.remove_net(net))
}
fn connect_pin(&mut self, pin: &Self::PinId, net: Option<Self::NetId>) -> Option<Self::NetId> {
self.perf_counters
.get(FnName::connect_pin)
.measure(|| self.chip.connect_pin(pin, net))
}
fn connect_pin_instance(
&mut self,
pin: &Self::PinInstId,
net: Option<Self::NetId>,
) -> Option<Self::NetId> {
self.perf_counters
.get(FnName::connect_pin_instance)
.measure(|| self.chip.connect_pin_instance(pin, net))
}
fn disconnect_pin(&mut self, pin: &Self::PinId) -> Option<Self::NetId> {
self.perf_counters
.get(FnName::disconnect_pin)
.measure(|| self.chip.disconnect_pin(pin))
}
fn disconnect_pin_instance(&mut self, pin_instance: &Self::PinInstId) -> Option<Self::NetId> {
self.perf_counters
.get(FnName::disconnect_pin_instance)
.measure(|| self.chip.disconnect_pin_instance(pin_instance))
}
}
impl<L: L2NBase> L2NBase for DBPerf<L> {
fn shapes_of_net(&self, net_id: &Self::NetId) -> Box<dyn Iterator<Item = Self::ShapeId> + '_> {
self.perf_counters
.get(FnName::shapes_of_net)
.measure(|| self.chip.shapes_of_net(net_id))
}
fn shapes_of_pin(&self, pin_id: &Self::PinId) -> Box<dyn Iterator<Item = Self::ShapeId> + '_> {
self.perf_counters
.get(FnName::shapes_of_pin)
.measure(|| self.chip.shapes_of_pin(pin_id))
}
fn get_net_of_shape(&self, shape_id: &Self::ShapeId) -> Option<Self::NetId> {
self.perf_counters
.get(FnName::get_net_of_shape)
.measure(|| self.chip.get_net_of_shape(shape_id))
}
fn get_pin_of_shape(&self, shape_id: &Self::ShapeId) -> Option<Self::PinId> {
self.perf_counters
.get(FnName::get_pin_of_shape)
.measure(|| self.chip.get_pin_of_shape(shape_id))
}
}
impl<L: L2NEdit> L2NEdit for DBPerf<L> {
fn set_pin_of_shape(
&mut self,
shape_id: &Self::ShapeId,
pin: Option<Self::PinId>,
) -> Option<Self::PinId> {
self.perf_counters
.get(FnName::set_pin_of_shape)
.measure(|| self.chip.set_pin_of_shape(shape_id, pin))
}
fn set_net_of_shape(
&mut self,
shape_id: &Self::ShapeId,
net: Option<Self::NetId>,
) -> Option<Self::NetId> {
self.perf_counters
.get(FnName::set_net_of_shape)
.measure(|| self.chip.set_net_of_shape(shape_id, net))
}
}
#[test]
fn test_dbperf() {
use crate::chip::Chip;
let mut chip = Chip::new();
let mut chip_perf = DBPerf::new(&mut chip);
let _cell = chip_perf.create_cell("A".into());
let _cell = chip_perf.create_cell("B".into());
let _cell = chip_perf.create_cell("C".into());
let create_cell_perf = chip_perf.get_stats(FnName::create_cell);
dbg!(create_cell_perf);
assert_eq!(create_cell_perf.num_calls, 3);
assert!(!create_cell_perf.total_time.is_zero());
assert!(create_cell_perf.min_time <= create_cell_perf.max_time);
assert!(create_cell_perf.min_time <= create_cell_perf.avg_time().unwrap());
assert!(create_cell_perf.avg_time().unwrap() <= create_cell_perf.max_time);
}