Skip to content

Commit

Permalink
ghw: small performance improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Mar 5, 2024
1 parent 76ad5fe commit 6dbc73f
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 62 deletions.
9 changes: 9 additions & 0 deletions examples/load_signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ use wellen::*;
struct Args {
#[arg(value_name = "FSTFILE", index = 1)]
filename: String,
#[arg(
long,
help = "only parse the file, but do not actually load the signals"
)]
skip_load: bool,
}

fn print_size_of_full_vs_reduced_names(hierarchy: &Hierarchy) {
Expand Down Expand Up @@ -80,6 +85,10 @@ fn main() {
);
print_size_of_full_vs_reduced_names(wave.hierarchy());

if args.skip_load {
return;
}

// load every signal individually
let mut signal_load_times = Vec::new();
let mut signal_sizes = Vec::new();
Expand Down
20 changes: 7 additions & 13 deletions src/ghw/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,28 +233,22 @@ impl GhwSignalInfo {
}
}

pub type GhwDecodeInfo = (GhwSignals, Vec<GhwVecInfo>);

/// Contains information needed in order to decode value changes.
#[derive(Debug, Default)]
pub struct GhwDecodeInfo {
pub struct GhwSignals {
/// Type and signal reference info. Indexed by `GhwSignalId`
signals: Vec<GhwSignalInfo>,
/// Vector info. Indexed by `GhwVecId`.
vectors: Vec<GhwVecInfo>,
}

impl GhwDecodeInfo {
pub fn new(signals: Vec<GhwSignalInfo>, vectors: Vec<GhwVecInfo>) -> Self {
Self { signals, vectors }
}
pub fn is_empty(&self) -> bool {
self.signals.is_empty()
impl GhwSignals {
pub fn new(signals: Vec<GhwSignalInfo>) -> Self {
Self { signals }
}
pub fn get_info(&self, signal_id: GhwSignalId) -> &GhwSignalInfo {
&self.signals[signal_id.index()]
}
pub fn vectors(&self) -> &[GhwVecInfo] {
&self.vectors
}
pub fn signal_len(&self) -> usize {
self.signals.len()
}
Expand Down Expand Up @@ -435,6 +429,6 @@ mod tests {
// This is in comparison to ghwdump which stores at least two 8-bit pointers per signal.
assert_eq!(std::mem::size_of::<GhwSignalInfo>(), 8);

assert_eq!(std::mem::size_of::<GhwVecInfo>(), 16);
assert_eq!(std::mem::size_of::<GhwVecInfo>(), 20);
}
}
30 changes: 15 additions & 15 deletions src/ghw/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub(crate) fn read_hierarchy(
) -> Result<(GhwDecodeInfo, Hierarchy)> {
let mut tables = GhwTables::default();
let mut strings = Vec::new();
let mut decode = GhwDecodeInfo::default();
let mut decode: Option<GhwDecodeInfo> = None;
let mut hb = HierarchyBuilder::new(FileFormat::Ghw);

// GHW seems to always uses fs
Expand Down Expand Up @@ -182,12 +182,12 @@ pub(crate) fn read_hierarchy(
GHW_HIERARCHY_SECTION => {
let dec = read_hierarchy_section(header, &mut tables, input, &mut hb)?;
debug_assert!(
decode.is_empty(),
decode.is_none(),
"unexpected second hierarchy section:\n{:?}\n{:?}",
decode,
dec
);
decode = dec;
decode = Some(dec);
}
GHW_END_OF_HEADER_SECTION => {
break; // done
Expand All @@ -200,7 +200,7 @@ pub(crate) fn read_hierarchy(
}
}
let hierarchy = hb.finish();
Ok((decode, hierarchy))
Ok((decode.unwrap(), hierarchy))
}

/// adds all enums to the hierarchy and
Expand Down Expand Up @@ -716,12 +716,12 @@ fn read_hierarchy_section(
// declared signals, may be composite
let expected_num_declared_vars = header.read_u32(&mut &hdr[8..12])?;
let max_signal_id = header.read_u32(&mut &hdr[12..16])?;
println!("Max signal id: {max_signal_id}");
println!("expected_num_declared_vars {expected_num_declared_vars}");
// println!("Max signal id: {max_signal_id}");
// println!("expected_num_declared_vars {expected_num_declared_vars}");

let mut num_declared_vars = 0;
let mut index_string_cache = IndexCache::new();
let mut signal_info = GhwSignals::new(max_signal_id);
let mut signal_info = GhwSignalTracker::new(max_signal_id);

loop {
let kind = GhwHierarchyKind::try_from_primitive(read_u8(input)?)?;
Expand Down Expand Up @@ -771,10 +771,10 @@ fn read_hierarchy_section(
}
}

println!("Wellen Signal Refs: {}", signal_info.signal_ref_count);
// println!("Wellen Signal Refs: {}", signal_info.signal_ref_count);
let decode_info = signal_info.into_decode_info();
println!("GHW Signals: {}", decode_info.signal_len());
println!("Vectors: {}", decode_info.vectors().len());
// println!("GHW Signals: {}", decode_info.0.signal_len());
// println!("Vectors: {}", decode_info.1.len());

Ok(decode_info)
}
Expand Down Expand Up @@ -865,7 +865,7 @@ fn read_hierarchy_var(
tables: &mut GhwTables,
input: &mut impl BufRead,
kind: GhwHierarchyKind,
signals: &mut GhwSignals,
signals: &mut GhwSignalTracker,
index_string_cache: &mut IndexCache,
h: &mut HierarchyBuilder,
) -> Result<()> {
Expand Down Expand Up @@ -906,7 +906,7 @@ fn get_index_string(
/// information that is needed in order to map the bit-wise signal encoding
/// in a GHW to the bit-vector signal encoding used in our `wellen` wavemem.
#[derive(Debug)]
struct GhwSignals {
struct GhwSignalTracker {
signals: Vec<Option<GhwSignalInfo>>,
signal_ref_count: usize,
vectors: Vec<GhwVecInfo>,
Expand All @@ -922,7 +922,7 @@ struct AliasInfo {
next: Option<NonZeroU32>,
}

impl GhwSignals {
impl GhwSignalTracker {
fn new(max_signal_id: u32) -> Self {
let signals = vec![None; max_signal_id as usize];
Self {
Expand All @@ -936,7 +936,7 @@ impl GhwSignals {
fn into_decode_info(self) -> GhwDecodeInfo {
let mut signals: Vec<_> = self.signals.into_iter().flatten().collect();
signals.shrink_to_fit();
GhwDecodeInfo::new(signals, self.vectors)
(GhwSignals::new(signals), self.vectors)
}

fn max_signal_id(&self) -> usize {
Expand Down Expand Up @@ -1096,7 +1096,7 @@ fn add_var(
tables: &GhwTables,
input: &mut impl BufRead,
kind: GhwHierarchyKind,
signals: &mut GhwSignals,
signals: &mut GhwSignalTracker,
index_string_cache: &mut IndexCache,
h: &mut HierarchyBuilder,
name: HierarchyStringId,
Expand Down
2 changes: 1 addition & 1 deletion src/ghw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ fn read_internal(input: &mut (impl BufRead + Seek)) -> std::result::Result<Wavef
// TODO: use actual section positions

let (decode_info, hierarchy) = hierarchy::read_hierarchy(&header, input)?;
let wave_mem = signals::read_signals(&header, &decode_info, &hierarchy, input)?;
let wave_mem = signals::read_signals(&header, decode_info, &hierarchy, input)?;
Ok(Waveform::new(hierarchy, wave_mem))
}
73 changes: 40 additions & 33 deletions src/ghw/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ use std::io::BufRead;
/// Reads the GHW signal values. `input` should be advanced until right after the end of hierarchy
pub(crate) fn read_signals(
header: &HeaderData,
info: &GhwDecodeInfo,
decode_info: GhwDecodeInfo,
hierarchy: &Hierarchy,
input: &mut impl BufRead,
) -> Result<Box<crate::wavemem::Reader>> {
let (info, vectors) = decode_info;
// TODO: multi-threading
let mut encoder = Encoder::new(hierarchy);
let mut vecs = VecBuffer::from_vec_info(info.vectors());
let mut vecs = VecBuffer::from_vec_info(vectors);

// loop over signal sections
loop {
Expand All @@ -26,9 +27,9 @@ pub(crate) fn read_signals(
// read_sm_hdr
match &mark {
GHW_SNAPSHOT_SECTION => {
read_snapshot_section(header, info, &mut vecs, &mut encoder, input)?
read_snapshot_section(header, &info, &mut vecs, &mut encoder, input)?
}
GHW_CYCLE_SECTION => read_cycle_section(header, info, &mut vecs, &mut encoder, input)?,
GHW_CYCLE_SECTION => read_cycle_section(header, &info, &mut vecs, &mut encoder, input)?,
GHW_DIRECTORY_SECTION => {
// skip the directory by reading it
let _ = read_directory(header, input)?;
Expand All @@ -49,7 +50,7 @@ pub(crate) fn read_signals(

fn read_snapshot_section(
header: &HeaderData,
info: &GhwDecodeInfo,
info: &GhwSignals,
vecs: &mut VecBuffer,
enc: &mut Encoder,
input: &mut impl BufRead,
Expand All @@ -74,7 +75,7 @@ fn read_snapshot_section(

fn read_cycle_section(
header: &HeaderData,
info: &GhwDecodeInfo,
info: &GhwSignals,
vecs: &mut VecBuffer,
enc: &mut Encoder,
input: &mut impl BufRead,
Expand Down Expand Up @@ -106,7 +107,7 @@ fn read_cycle_section(
}

fn read_cycle_signals(
info: &GhwDecodeInfo,
info: &GhwSignals,
vecs: &mut VecBuffer,
enc: &mut Encoder,
input: &mut impl BufRead,
Expand Down Expand Up @@ -138,7 +139,7 @@ fn finish_time_step(vecs: &mut VecBuffer, enc: &mut Encoder) {
}

fn read_signal_value(
info: &GhwDecodeInfo,
info: &GhwSignals,
signal_id: GhwSignalId,
vecs: &mut VecBuffer,
enc: &mut Encoder,
Expand Down Expand Up @@ -167,18 +168,16 @@ fn read_signal_value(
};

let vec_id = signal_info.vec_id().unwrap();
let vec_info = &info.vectors()[vec_id.index()];
let bit = (signal_id.index() - vec_info.min().index()) as u32;

// check to see if we already had a change to this same bit in the current time step
if vecs.is_second_change(vec_id, bit, value) {
if vecs.is_second_change(vec_id, signal_id, value) {
// immediately dispatch the change to properly reflect the delta cycle
let data = vecs.get_full_value_and_clear_changes(vec_id);
enc.raw_value_change(signal_ref, data, states);
}

// update value
vecs.update_value(vec_id, bit, value);
vecs.update_value(vec_id, signal_id, value);

// check to see if we need to report a change
if vecs.full_signal_has_changed(vec_id) {
Expand Down Expand Up @@ -224,6 +223,7 @@ struct VecBufferInfo {
bits: u32,
states: States,
signal_ref: SignalRef,
min_index: u32,
}

impl VecBufferInfo {
Expand All @@ -242,28 +242,33 @@ impl VecBufferInfo {
}

impl VecBuffer {
fn from_vec_info(vectors: &[GhwVecInfo]) -> Self {
let mut info = Vec::with_capacity(vectors.len());
fn from_vec_info(vectors: Vec<GhwVecInfo>) -> Self {
let mut data_start = 0;
let mut bit_change_start = 0;

for vector in vectors.iter() {
let bits = vector.bits();
let states = if vector.is_two_state() {
States::Two
} else {
States::Nine
};
info.push(VecBufferInfo {
data_start: data_start as u32,
bit_change_start: bit_change_start as u32,
bits,
states,
signal_ref: vector.signal_ref(),
});
data_start += (bits as usize).div_ceil(states.bits_in_a_byte());
bit_change_start += (bits as usize).div_ceil(8);
}
let mut info = vectors
.into_iter()
.map(|vector| {
let bits = vector.bits();
let states = if vector.is_two_state() {
States::Two
} else {
States::Nine
};
let info = VecBufferInfo {
data_start: data_start as u32,
bit_change_start: bit_change_start as u32,
bits,
states,
signal_ref: vector.signal_ref(),
min_index: vector.min().index() as u32,
};
data_start += (bits as usize).div_ceil(states.bits_in_a_byte());
bit_change_start += (bits as usize).div_ceil(8);
info
})
.collect::<Vec<_>>();
info.shrink_to_fit();

let data = vec![0; data_start];
let bit_change = vec![0; bit_change_start];
Expand Down Expand Up @@ -292,14 +297,16 @@ impl VecBuffer {
}

#[inline]
fn is_second_change(&self, vector_id: GhwVecId, bit: u32, value: u8) -> bool {
fn is_second_change(&self, vector_id: GhwVecId, signal_id: GhwSignalId, value: u8) -> bool {
let info = &self.info[vector_id.index()];
let bit = signal_id.index() as u32 - info.min_index;
self.has_bit_changed(info, bit) && self.get_value(info, bit) != value
}

#[inline]
fn update_value(&mut self, vector_id: GhwVecId, bit: u32, value: u8) {
fn update_value(&mut self, vector_id: GhwVecId, signal_id: GhwSignalId, value: u8) {
let info = &self.info[vector_id.index()];
let bit = signal_id.index() as u32 - info.min_index;
let is_a_real_change = self.get_value(info, bit) != value;
if is_a_real_change {
Self::mark_bit_changed(&mut self.bit_change, info, bit);
Expand Down
23 changes: 23 additions & 0 deletions src/wavemem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,30 @@ fn check_min_state(value: &[u8], states: States) -> States {
States::from_value(union)
}

/// picks a specialized compress implementation
fn compress(value: &[u8], in_states: States, out_states: States, bits: usize, out: &mut Vec<u8>) {
match (in_states, out_states) {
(States::Nine, States::Two) => {
compress_template(value, States::Nine, States::Two, bits, out)
}
(States::Four, States::Two) => {
compress_template(value, States::Four, States::Two, bits, out)
}
(States::Nine, States::Four) => {
compress_template(value, States::Nine, States::Four, bits, out)
}
_ => unreachable!("Cannot compress {in_states:?} => {out_states:?}"),
}
}

#[inline]
fn compress_template(
value: &[u8],
in_states: States,
out_states: States,
bits: usize,
out: &mut Vec<u8>,
) {
debug_assert!(in_states.bits_in_a_byte() < out_states.bits_in_a_byte());
let mut working_byte = 0u8;
for bit in (0..bits).rev() {
Expand Down

0 comments on commit 6dbc73f

Please sign in to comment.