mirror of
https://github.com/cberner/raptorq.git
synced 2024-07-01 03:01:48 +00:00
Add operation vectors for better encoding performance
Using stored operation vectors when generating intermediate symbols make encoding around three times faster (depends on block size). Signed-off-by: Anders Martinsson <anders.martinsson@intinor.se>
This commit is contained in:
parent
bdf5627e4c
commit
04786d26fd
@ -77,7 +77,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
"encode 10KB",
|
||||
Benchmark::new("", move |b| {
|
||||
b.iter(|| {
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &encode_data);
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &encode_data, None);
|
||||
return encoder.source_packets();
|
||||
})
|
||||
})
|
||||
@ -89,7 +89,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
"roundtrip 10KB",
|
||||
Benchmark::new("", move |b| {
|
||||
b.iter(|| {
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &roundtrip_data);
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &roundtrip_data, None);
|
||||
let mut decoder = SourceBlockDecoder::new(1, symbol_size, elements as u64);
|
||||
return decoder.decode(encoder.source_packets());
|
||||
})
|
||||
@ -102,7 +102,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
"roundtrip repair 10KB",
|
||||
Benchmark::new("", move |b| {
|
||||
b.iter(|| {
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &repair_data);
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &repair_data, None);
|
||||
let repair_packets = (elements / symbol_size as usize) as u32;
|
||||
let mut decoder = SourceBlockDecoder::new(1, symbol_size, elements as u64);
|
||||
return decoder.decode(encoder.repair_packets(0, repair_packets));
|
||||
|
@ -21,7 +21,7 @@ fn benchmark(symbol_size: u16, overhead: f64) -> u64 {
|
||||
}
|
||||
|
||||
let iterations = TARGET_TOTAL_BYTES / elements;
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &data);
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &data, None);
|
||||
let elements_and_overhead = (symbol_count as f64 * (1.0 + overhead)) as u32;
|
||||
let mut packets =
|
||||
encoder.repair_packets(0, (iterations as u32 * elements_and_overhead) as u32);
|
||||
|
@ -1,5 +1,6 @@
|
||||
use rand::Rng;
|
||||
use raptorq::SourceBlockEncoder;
|
||||
use raptorq::SourceBlockEncoderCache;
|
||||
use std::time::Instant;
|
||||
|
||||
const TARGET_TOTAL_BYTES: usize = 128 * 1024 * 1024;
|
||||
@ -11,10 +12,8 @@ fn black_box(value: u64) {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn benchmark(symbol_size: u16, cache: Option<&SourceBlockEncoderCache>) -> u64 {
|
||||
let mut black_box_value = 0;
|
||||
let symbol_size = 1280;
|
||||
println!("Symbol size: {} bytes", symbol_size);
|
||||
for symbol_count in SYMBOL_COUNTS.iter() {
|
||||
let elements = symbol_count * symbol_size as usize;
|
||||
let mut data: Vec<u8> = vec![0; elements];
|
||||
@ -22,10 +21,15 @@ fn main() {
|
||||
data[i] = rand::thread_rng().gen();
|
||||
}
|
||||
|
||||
if cache.is_some() {
|
||||
// Create and store the operation vector to measure performance when the cache is in use for all blocks.
|
||||
SourceBlockEncoder::new(1, symbol_size, &data, cache);
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
let iterations = TARGET_TOTAL_BYTES / elements;
|
||||
for _ in 0..iterations {
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &data);
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size, &data, cache);
|
||||
let packets = encoder.repair_packets(0, 1);
|
||||
black_box_value += packets[0].data()[0] as u64;
|
||||
}
|
||||
@ -40,5 +44,21 @@ fn main() {
|
||||
throughput
|
||||
);
|
||||
}
|
||||
black_box(black_box_value);
|
||||
return black_box_value;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let symbol_size = 1280;
|
||||
println!(
|
||||
"Symbol size: {} bytes (without operation vectors)",
|
||||
symbol_size
|
||||
);
|
||||
black_box(benchmark(symbol_size, None));
|
||||
println!();
|
||||
let cache = SourceBlockEncoderCache::new();
|
||||
println!(
|
||||
"Symbol size: {} bytes (with operation vectors)",
|
||||
symbol_size
|
||||
);
|
||||
black_box(benchmark(symbol_size, Some(&cache)));
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ fn main() {
|
||||
);
|
||||
|
||||
let symbols = vec![Symbol::zero(1usize); a.width()];
|
||||
let mut decoder = IntermediateSymbolDecoder::new(a, hdpc, symbols, num_symbols);
|
||||
let mut decoder = IntermediateSymbolDecoder::new(a, hdpc, symbols, num_symbols, false);
|
||||
println!(
|
||||
"Initial memory usage: {}KB",
|
||||
decoder.get_non_symbol_bytes() / 1024
|
||||
|
@ -156,9 +156,10 @@ impl SourceBlockDecoder {
|
||||
hdpc_rows,
|
||||
symbols,
|
||||
self.source_block_symbols,
|
||||
false,
|
||||
) {
|
||||
None => return None,
|
||||
Some(s) => s,
|
||||
(None, _) => return None,
|
||||
(Some(s), _) => s,
|
||||
};
|
||||
|
||||
let mut result = vec![];
|
||||
@ -295,6 +296,7 @@ mod codec_tests {
|
||||
use crate::Encoder;
|
||||
use crate::SourceBlockDecoder;
|
||||
use crate::SourceBlockEncoder;
|
||||
use crate::SourceBlockEncoderCache;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::Rng;
|
||||
|
||||
@ -375,7 +377,7 @@ mod codec_tests {
|
||||
println!("Completed {} symbols", symbol_count)
|
||||
}
|
||||
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size as u16, &data);
|
||||
let encoder = SourceBlockEncoder::new(1, symbol_size as u16, &data, None);
|
||||
|
||||
let mut decoder = SourceBlockDecoder::new(1, symbol_size as u16, elements as u64);
|
||||
decoder.set_sparse_threshold(sparse_threshold);
|
||||
@ -393,26 +395,43 @@ mod codec_tests {
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn repair_dense_extended() {
|
||||
repair(99_999, 5000, true);
|
||||
repair(99_999, 5000, true, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn repair_sparse_extended() {
|
||||
repair(0, 56403, true);
|
||||
repair(0, 56403, true, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repair_dense() {
|
||||
repair(99_999, 50, false);
|
||||
repair(99_999, 50, false, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repair_sparse() {
|
||||
repair(0, 50, false);
|
||||
repair(0, 50, false, None);
|
||||
}
|
||||
|
||||
fn repair(sparse_threshold: u32, max_symbols: usize, progress: bool) {
|
||||
#[test]
|
||||
fn repair_dense_cache() {
|
||||
let cache = SourceBlockEncoderCache::new();
|
||||
repair(99_999, 50, false, Some(&cache));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repair_sparse_cache() {
|
||||
let cache = SourceBlockEncoderCache::new();
|
||||
repair(0, 50, false, Some(&cache));
|
||||
}
|
||||
|
||||
fn repair(
|
||||
sparse_threshold: u32,
|
||||
max_symbols: usize,
|
||||
progress: bool,
|
||||
cache: Option<&SourceBlockEncoderCache>,
|
||||
) {
|
||||
let symbol_size = 8;
|
||||
for symbol_count in 1..=max_symbols {
|
||||
let elements = symbol_size * symbol_count;
|
||||
@ -425,7 +444,7 @@ mod codec_tests {
|
||||
println!("[repair] Completed {} symbols", symbol_count)
|
||||
}
|
||||
|
||||
let encoder = SourceBlockEncoder::new(1, 8, &data);
|
||||
let encoder = SourceBlockEncoder::new(1, 8, &data, cache);
|
||||
|
||||
let mut decoder = SourceBlockDecoder::new(1, 8, elements as u64);
|
||||
decoder.set_sparse_threshold(sparse_threshold);
|
||||
|
122
src/encoder.rs
122
src/encoder.rs
@ -4,6 +4,7 @@ use crate::base::EncodingPacket;
|
||||
use crate::base::PayloadId;
|
||||
use crate::constraint_matrix::generate_constraint_matrix;
|
||||
use crate::matrix::DenseBinaryMatrix;
|
||||
use crate::operation_vector::{perform_op, SymbolOps};
|
||||
use crate::pi_solver::fused_inverse_mul_symbols;
|
||||
use crate::sparse_matrix::SparseBinaryMatrix;
|
||||
use crate::symbol::Symbol;
|
||||
@ -16,6 +17,8 @@ use crate::systematic_constants::num_pi_symbols;
|
||||
use crate::systematic_constants::{calculate_p1, systematic_index};
|
||||
use crate::ObjectTransmissionInformation;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub const SPARSE_MATRIX_THRESHOLD: u32 = 250;
|
||||
|
||||
@ -39,6 +42,7 @@ impl Encoder {
|
||||
assert_eq!(1, config.sub_blocks());
|
||||
// let (tl, ts, nl, ns) = partition((config.symbol_size() / config.alignment() as u16) as u32, config.sub_blocks());
|
||||
|
||||
let cache = SourceBlockEncoderCache::new();
|
||||
let mut data_index = 0;
|
||||
let mut blocks = vec![];
|
||||
for i in 0..zl {
|
||||
@ -47,6 +51,7 @@ impl Encoder {
|
||||
i as u8,
|
||||
config.symbol_size(),
|
||||
&data[data_index..(data_index + offset)],
|
||||
Some(&cache),
|
||||
));
|
||||
data_index += offset;
|
||||
}
|
||||
@ -58,6 +63,7 @@ impl Encoder {
|
||||
i as u8,
|
||||
config.symbol_size(),
|
||||
&data[data_index..(data_index + offset)],
|
||||
Some(&cache),
|
||||
));
|
||||
} else {
|
||||
// Should only be possible when Kt * T > F. See third to last paragraph in section 4.4.1.2
|
||||
@ -72,6 +78,7 @@ impl Encoder {
|
||||
i as u8,
|
||||
config.symbol_size(),
|
||||
&padded,
|
||||
Some(&cache),
|
||||
));
|
||||
}
|
||||
data_index += offset;
|
||||
@ -98,6 +105,18 @@ impl Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SourceBlockEncoderCache {
|
||||
cache: Arc<RwLock<HashMap<usize, Vec<SymbolOps>>>>,
|
||||
}
|
||||
|
||||
impl SourceBlockEncoderCache {
|
||||
pub fn new() -> SourceBlockEncoderCache {
|
||||
let cache = Arc::new(RwLock::new(HashMap::new()));
|
||||
SourceBlockEncoderCache { cache }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SourceBlockEncoder {
|
||||
source_block_id: u8,
|
||||
@ -106,17 +125,60 @@ pub struct SourceBlockEncoder {
|
||||
}
|
||||
|
||||
impl SourceBlockEncoder {
|
||||
pub fn new(source_block_id: u8, symbol_size: u16, data: &[u8]) -> SourceBlockEncoder {
|
||||
pub fn new(
|
||||
source_block_id: u8,
|
||||
symbol_size: u16,
|
||||
data: &[u8],
|
||||
cache: Option<&SourceBlockEncoderCache>,
|
||||
) -> SourceBlockEncoder {
|
||||
assert_eq!(data.len() % symbol_size as usize, 0);
|
||||
let source_symbols: Vec<Symbol> = data
|
||||
.chunks(symbol_size as usize)
|
||||
.map(|x| Symbol::new(Vec::from(x)))
|
||||
.collect();
|
||||
let intermediate_symbols = gen_intermediate_symbols(
|
||||
&source_symbols,
|
||||
symbol_size as usize,
|
||||
SPARSE_MATRIX_THRESHOLD,
|
||||
);
|
||||
|
||||
let intermediate_symbols = match cache {
|
||||
Some(c) => {
|
||||
let key = source_symbols.len();
|
||||
let read_map = c.cache.read().unwrap();
|
||||
let value = read_map.get(&key);
|
||||
|
||||
match value {
|
||||
None => {
|
||||
drop(read_map);
|
||||
let (is, ops_vec) = gen_intermediate_symbols(
|
||||
&source_symbols,
|
||||
symbol_size as usize,
|
||||
SPARSE_MATRIX_THRESHOLD,
|
||||
true,
|
||||
);
|
||||
let mut write_map = c.cache.write().unwrap();
|
||||
write_map.insert(key, ops_vec.unwrap());
|
||||
drop(write_map);
|
||||
is.unwrap()
|
||||
}
|
||||
Some(operation_vector) => {
|
||||
let is = gen_intermediate_symbols_ops_vec(
|
||||
&source_symbols,
|
||||
symbol_size as usize,
|
||||
&(*operation_vector),
|
||||
);
|
||||
drop(read_map);
|
||||
is
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let (is, _ops_vec) = gen_intermediate_symbols(
|
||||
&source_symbols,
|
||||
symbol_size as usize,
|
||||
SPARSE_MATRIX_THRESHOLD,
|
||||
false,
|
||||
);
|
||||
is.unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
SourceBlockEncoder {
|
||||
source_block_id,
|
||||
source_symbols,
|
||||
@ -162,17 +224,15 @@ impl SourceBlockEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
// See section 5.3.3.4
|
||||
#[allow(non_snake_case)]
|
||||
fn gen_intermediate_symbols(
|
||||
fn create_d(
|
||||
source_block: &[Symbol],
|
||||
symbol_size: usize,
|
||||
sparse_threshold: u32,
|
||||
extended_source_symbols: usize,
|
||||
) -> Vec<Symbol> {
|
||||
let L = num_intermediate_symbols(source_block.len() as u32);
|
||||
let S = num_ldpc_symbols(source_block.len() as u32);
|
||||
let H = num_hdpc_symbols(source_block.len() as u32);
|
||||
let extended_source_symbols = extended_source_block_symbols(source_block.len() as u32);
|
||||
|
||||
let mut D = Vec::with_capacity(L as usize);
|
||||
for _ in 0..(S + H) {
|
||||
@ -186,19 +246,47 @@ fn gen_intermediate_symbols(
|
||||
D.push(Symbol::zero(symbol_size));
|
||||
}
|
||||
assert_eq!(D.len(), L as usize);
|
||||
D
|
||||
}
|
||||
|
||||
// See section 5.3.3.4
|
||||
#[allow(non_snake_case)]
|
||||
fn gen_intermediate_symbols(
|
||||
source_block: &[Symbol],
|
||||
symbol_size: usize,
|
||||
sparse_threshold: u32,
|
||||
store_operations: bool,
|
||||
) -> (Option<Vec<Symbol>>, Option<Vec<SymbolOps>>) {
|
||||
let extended_source_symbols = extended_source_block_symbols(source_block.len() as u32);
|
||||
let D = create_d(source_block, symbol_size, extended_source_symbols as usize);
|
||||
|
||||
let indices: Vec<u32> = (0..extended_source_symbols).collect();
|
||||
if extended_source_symbols >= sparse_threshold {
|
||||
let (A, hdpc) =
|
||||
generate_constraint_matrix::<SparseBinaryMatrix>(extended_source_symbols, &indices);
|
||||
return fused_inverse_mul_symbols(A, hdpc, D, extended_source_symbols).unwrap();
|
||||
return fused_inverse_mul_symbols(A, hdpc, D, extended_source_symbols, store_operations);
|
||||
} else {
|
||||
let (A, hdpc) =
|
||||
generate_constraint_matrix::<DenseBinaryMatrix>(extended_source_symbols, &indices);
|
||||
return fused_inverse_mul_symbols(A, hdpc, D, extended_source_symbols).unwrap();
|
||||
return fused_inverse_mul_symbols(A, hdpc, D, extended_source_symbols, store_operations);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn gen_intermediate_symbols_ops_vec(
|
||||
source_block: &[Symbol],
|
||||
symbol_size: usize,
|
||||
operation_vector: &[SymbolOps],
|
||||
) -> Vec<Symbol> {
|
||||
let extended_source_symbols = extended_source_block_symbols(source_block.len() as u32);
|
||||
let mut D = create_d(source_block, symbol_size, extended_source_symbols as usize);
|
||||
|
||||
for op in operation_vector {
|
||||
perform_op(op, &mut D);
|
||||
}
|
||||
D
|
||||
}
|
||||
|
||||
// Enc[] function, as defined in section 5.3.5.3
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn enc(
|
||||
@ -287,8 +375,10 @@ mod tests {
|
||||
|
||||
fn enc_constraint(sparse_threshold: u32) {
|
||||
let source_symbols = gen_test_symbols();
|
||||
let intermediate_symbols =
|
||||
gen_intermediate_symbols(&source_symbols, SYMBOL_SIZE, sparse_threshold);
|
||||
|
||||
let (is, _ops_vec) =
|
||||
gen_intermediate_symbols(&source_symbols, SYMBOL_SIZE, sparse_threshold, false);
|
||||
let intermediate_symbols = is.unwrap();
|
||||
|
||||
let lt_symbols = num_lt_symbols(NUM_SYMBOLS);
|
||||
let sys_index = systematic_index(NUM_SYMBOLS);
|
||||
@ -313,7 +403,9 @@ mod tests {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn ldpc_constraint(sparse_threshold: u32) {
|
||||
let C = gen_intermediate_symbols(&gen_test_symbols(), SYMBOL_SIZE, sparse_threshold);
|
||||
let (is, _ops_vec) =
|
||||
gen_intermediate_symbols(&gen_test_symbols(), SYMBOL_SIZE, sparse_threshold, false);
|
||||
let C = is.unwrap();
|
||||
let S = num_ldpc_symbols(NUM_SYMBOLS) as usize;
|
||||
let P = num_pi_symbols(NUM_SYMBOLS) as usize;
|
||||
let W = num_lt_symbols(NUM_SYMBOLS) as usize;
|
||||
|
@ -11,6 +11,7 @@ mod matrix;
|
||||
mod octet;
|
||||
mod octet_matrix;
|
||||
mod octets;
|
||||
mod operation_vector;
|
||||
mod pi_solver;
|
||||
mod rng;
|
||||
mod sparse_matrix;
|
||||
@ -26,6 +27,7 @@ pub use crate::decoder::Decoder;
|
||||
pub use crate::decoder::SourceBlockDecoder;
|
||||
pub use crate::encoder::Encoder;
|
||||
pub use crate::encoder::SourceBlockEncoder;
|
||||
pub use crate::encoder::SourceBlockEncoderCache;
|
||||
|
||||
#[cfg(feature = "benchmarking")]
|
||||
pub use crate::constraint_matrix::generate_constraint_matrix;
|
||||
|
199
src/operation_vector.rs
Normal file
199
src/operation_vector.rs
Normal file
@ -0,0 +1,199 @@
|
||||
use crate::octet::Octet;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::util::get_both_indices;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
pub enum SymbolOps {
|
||||
AddAssign {
|
||||
dest: usize,
|
||||
src: usize,
|
||||
},
|
||||
MulAssign {
|
||||
dest: usize,
|
||||
scalar: Octet,
|
||||
},
|
||||
FMA {
|
||||
dest: usize,
|
||||
src: usize,
|
||||
scalar: Octet,
|
||||
},
|
||||
Reorder {
|
||||
order: Vec<usize>,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn perform_op(op: &SymbolOps, symbols: &mut Vec<Symbol>) {
|
||||
match op {
|
||||
SymbolOps::AddAssign { dest, src } => {
|
||||
let (dest, temp) = get_both_indices(symbols, *dest, *src);
|
||||
*dest += temp;
|
||||
}
|
||||
SymbolOps::MulAssign { dest, scalar } => {
|
||||
symbols[*dest].mulassign_scalar(scalar);
|
||||
}
|
||||
SymbolOps::FMA { dest, src, scalar } => {
|
||||
let (dest, temp) = get_both_indices(symbols, *dest, *src);
|
||||
dest.fused_addassign_mul_scalar(temp, scalar);
|
||||
}
|
||||
SymbolOps::Reorder { order } => {
|
||||
/* TODO: Reorder is the last step of the algorithm. It should be
|
||||
* possible to move reorder to be the first step and use when
|
||||
* creating D (place all rows in correct position before
|
||||
* calculations). This will however force an update on all
|
||||
* row-numbers used in all other "Operations". */
|
||||
let mut temp_symbols: Vec<Option<Symbol>> = symbols.drain(..).map(Some).collect();
|
||||
for row_index in order.iter() {
|
||||
symbols.push(temp_symbols[*row_index].take().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::Rng;
|
||||
|
||||
use crate::octet::Octet;
|
||||
use crate::operation_vector::{perform_op, SymbolOps};
|
||||
use crate::symbol::Symbol;
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let rows = 2;
|
||||
let symbol_size = 1316;
|
||||
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
|
||||
|
||||
for _i in 0..rows {
|
||||
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
|
||||
for j in 0..symbol_size {
|
||||
symbol_data[j] = rand::thread_rng().gen();
|
||||
}
|
||||
let symbol = Symbol::new(symbol_data);
|
||||
data.push(symbol);
|
||||
}
|
||||
|
||||
let mut data0: Vec<u8> = vec![0; symbol_size];
|
||||
let mut data1: Vec<u8> = vec![0; symbol_size];
|
||||
let mut result: Vec<u8> = vec![0; symbol_size];
|
||||
for i in 0..symbol_size {
|
||||
data0[i] = data[0].as_bytes()[i];
|
||||
data1[i] = data[1].as_bytes()[i];
|
||||
result[i] = data0[i] ^ data1[i];
|
||||
}
|
||||
let mut symbol0 = Symbol::new(data0);
|
||||
let symbol1 = Symbol::new(data1);
|
||||
|
||||
symbol0 += &symbol1;
|
||||
|
||||
perform_op(&SymbolOps::AddAssign { dest: 0, src: 1 }, &mut data);
|
||||
assert_eq!(result, data[0].as_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_mul() {
|
||||
let rows = 2;
|
||||
let symbol_size = 1316;
|
||||
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
|
||||
|
||||
for _i in 0..rows {
|
||||
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
|
||||
for j in 0..symbol_size {
|
||||
symbol_data[j] = rand::thread_rng().gen();
|
||||
}
|
||||
let symbol = Symbol::new(symbol_data);
|
||||
data.push(symbol);
|
||||
}
|
||||
|
||||
let value = 173;
|
||||
let mut data0: Vec<u8> = vec![0; symbol_size];
|
||||
let mut data1: Vec<u8> = vec![0; symbol_size];
|
||||
let mut result: Vec<u8> = vec![0; symbol_size];
|
||||
for i in 0..symbol_size {
|
||||
data0[i] = data[0].as_bytes()[i];
|
||||
data1[i] = data[1].as_bytes()[i];
|
||||
result[i] = data0[i] ^ (Octet::new(data1[i]) * Octet::new(value)).byte();
|
||||
}
|
||||
|
||||
perform_op(
|
||||
&SymbolOps::FMA {
|
||||
dest: 0,
|
||||
src: 1,
|
||||
scalar: Octet::new(value),
|
||||
},
|
||||
&mut data,
|
||||
);
|
||||
assert_eq!(result, data[0].as_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
let rows = 1;
|
||||
let symbol_size = 1316;
|
||||
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
|
||||
|
||||
for _i in 0..rows {
|
||||
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
|
||||
for j in 0..symbol_size {
|
||||
symbol_data[j] = rand::thread_rng().gen();
|
||||
}
|
||||
let symbol = Symbol::new(symbol_data);
|
||||
data.push(symbol);
|
||||
}
|
||||
|
||||
let value = 215;
|
||||
let mut data0: Vec<u8> = vec![0; symbol_size];
|
||||
let mut result: Vec<u8> = vec![0; symbol_size];
|
||||
for i in 0..symbol_size {
|
||||
data0[i] = data[0].as_bytes()[i];
|
||||
result[i] = (Octet::new(data0[i]) * Octet::new(value)).byte();
|
||||
}
|
||||
|
||||
perform_op(
|
||||
&SymbolOps::MulAssign {
|
||||
dest: 0,
|
||||
scalar: Octet::new(value),
|
||||
},
|
||||
&mut data,
|
||||
);
|
||||
assert_eq!(result, data[0].as_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reorder() {
|
||||
let rows = 10;
|
||||
let symbol_size = 10;
|
||||
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
|
||||
|
||||
for i in 0..rows {
|
||||
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
|
||||
for j in 0..symbol_size {
|
||||
symbol_data[j] = i as u8;
|
||||
}
|
||||
let symbol = Symbol::new(symbol_data);
|
||||
data.push(symbol);
|
||||
}
|
||||
|
||||
assert_eq!(data[0].as_bytes()[0], 0);
|
||||
assert_eq!(data[1].as_bytes()[0], 1);
|
||||
assert_eq!(data[2].as_bytes()[0], 2);
|
||||
assert_eq!(data[9].as_bytes()[0], 9);
|
||||
|
||||
perform_op(
|
||||
&SymbolOps::Reorder {
|
||||
order: vec![9, 7, 5, 3, 1, 8, 0, 6, 2, 4],
|
||||
},
|
||||
&mut data,
|
||||
);
|
||||
assert_eq!(data[0].as_bytes()[0], 9);
|
||||
assert_eq!(data[1].as_bytes()[0], 7);
|
||||
assert_eq!(data[2].as_bytes()[0], 5);
|
||||
assert_eq!(data[3].as_bytes()[0], 3);
|
||||
assert_eq!(data[4].as_bytes()[0], 1);
|
||||
assert_eq!(data[5].as_bytes()[0], 8);
|
||||
assert_eq!(data[6].as_bytes()[0], 0);
|
||||
assert_eq!(data[7].as_bytes()[0], 6);
|
||||
assert_eq!(data[8].as_bytes()[0], 2);
|
||||
assert_eq!(data[9].as_bytes()[0], 4);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ use crate::arraymap::{U16ArrayMap, U32VecMap};
|
||||
use crate::matrix::BinaryMatrix;
|
||||
use crate::octet::Octet;
|
||||
use crate::octet_matrix::DenseOctetMatrix;
|
||||
use crate::operation_vector::SymbolOps;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::systematic_constants::num_hdpc_symbols;
|
||||
use crate::systematic_constants::num_intermediate_symbols;
|
||||
@ -12,23 +13,6 @@ use crate::util::get_both_indices;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::mem::size_of;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
enum SymbolOps {
|
||||
AddAssign {
|
||||
dest: usize,
|
||||
src: usize,
|
||||
},
|
||||
MulAssign {
|
||||
dest: usize,
|
||||
scalar: Octet,
|
||||
},
|
||||
FMA {
|
||||
dest: usize,
|
||||
src: usize,
|
||||
scalar: Octet,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
struct FirstPhaseRowSelectionStats {
|
||||
original_degree: U16ArrayMap,
|
||||
@ -362,6 +346,7 @@ pub struct IntermediateSymbolDecoder<T: BinaryMatrix> {
|
||||
// Operations on D are deferred to the end of the codec to improve cache hits
|
||||
deferred_D_ops: Vec<SymbolOps>,
|
||||
num_source_symbols: u32,
|
||||
store_operations: bool,
|
||||
debug_symbol_mul_ops: u32,
|
||||
debug_symbol_add_ops: u32,
|
||||
debug_symbol_mul_ops_by_phase: Vec<u32>,
|
||||
@ -375,6 +360,7 @@ impl<T: BinaryMatrix> IntermediateSymbolDecoder<T> {
|
||||
hdpc_rows: DenseOctetMatrix,
|
||||
symbols: Vec<Symbol>,
|
||||
num_source_symbols: u32,
|
||||
store_operations: bool,
|
||||
) -> IntermediateSymbolDecoder<T> {
|
||||
assert!(matrix.width() <= symbols.len());
|
||||
assert_eq!(matrix.height(), symbols.len());
|
||||
@ -411,6 +397,7 @@ impl<T: BinaryMatrix> IntermediateSymbolDecoder<T> {
|
||||
L: intermediate_symbols,
|
||||
deferred_D_ops: Vec::with_capacity(70 * intermediate_symbols),
|
||||
num_source_symbols,
|
||||
store_operations,
|
||||
debug_symbol_mul_ops: 0,
|
||||
debug_symbol_add_ops: 0,
|
||||
debug_symbol_mul_ops_by_phase: vec![0; 5],
|
||||
@ -446,6 +433,7 @@ impl<T: BinaryMatrix> IntermediateSymbolDecoder<T> {
|
||||
let (dest, temp) = get_both_indices(&mut self.D, dest, src);
|
||||
dest.fused_addassign_mul_scalar(&temp, &scalar);
|
||||
}
|
||||
SymbolOps::Reorder { order: _order } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1122,23 +1110,29 @@ impl<T: BinaryMatrix> IntermediateSymbolDecoder<T> {
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn execute(&mut self) -> Option<Vec<Symbol>> {
|
||||
pub fn execute(&mut self) -> (Option<Vec<Symbol>>, Option<Vec<SymbolOps>>) {
|
||||
self.X.disable_column_acccess_acceleration();
|
||||
|
||||
if !self.first_phase() {
|
||||
return None;
|
||||
return (None, None);
|
||||
}
|
||||
|
||||
self.A.disable_column_acccess_acceleration();
|
||||
|
||||
if !self.second_phase() {
|
||||
return None;
|
||||
return (None, None);
|
||||
}
|
||||
|
||||
self.third_phase();
|
||||
self.fourth_phase();
|
||||
self.fifth_phase();
|
||||
|
||||
let mut operation_vector: Vec<SymbolOps> = if self.store_operations {
|
||||
self.deferred_D_ops.clone()
|
||||
} else {
|
||||
Vec::with_capacity(0)
|
||||
};
|
||||
|
||||
self.apply_deferred_symbol_ops();
|
||||
|
||||
// See end of section 5.4.2.1
|
||||
@ -1157,7 +1151,16 @@ impl<T: BinaryMatrix> IntermediateSymbolDecoder<T> {
|
||||
removable_D.push(None);
|
||||
result.push(removable_D.swap_remove(index_mapping[i]).unwrap());
|
||||
}
|
||||
Some(result)
|
||||
if self.store_operations {
|
||||
let mut reorder = Vec::with_capacity(self.L);
|
||||
for i in index_mapping.iter().take(self.L) {
|
||||
reorder.push(*i);
|
||||
}
|
||||
operation_vector.push(SymbolOps::Reorder { order: reorder });
|
||||
return (Some(result), Some(operation_vector));
|
||||
}
|
||||
|
||||
(Some(result), None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1168,8 +1171,16 @@ pub fn fused_inverse_mul_symbols<T: BinaryMatrix>(
|
||||
hdpc_rows: DenseOctetMatrix,
|
||||
symbols: Vec<Symbol>,
|
||||
num_source_symbols: u32,
|
||||
) -> Option<Vec<Symbol>> {
|
||||
IntermediateSymbolDecoder::new(matrix, hdpc_rows, symbols, num_source_symbols).execute()
|
||||
store_operations: bool,
|
||||
) -> (Option<Vec<Symbol>>, Option<Vec<SymbolOps>>) {
|
||||
IntermediateSymbolDecoder::new(
|
||||
matrix,
|
||||
hdpc_rows,
|
||||
symbols,
|
||||
num_source_symbols,
|
||||
store_operations,
|
||||
)
|
||||
.execute()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -1193,7 +1204,7 @@ mod tests {
|
||||
let indices: Vec<u32> = (0..num_symbols).collect();
|
||||
let (a, hdpc) = generate_constraint_matrix::<DenseBinaryMatrix>(num_symbols, &indices);
|
||||
let symbols = vec![Symbol::zero(1usize); a.width()];
|
||||
let mut decoder = IntermediateSymbolDecoder::new(a, hdpc, symbols, num_symbols);
|
||||
let mut decoder = IntermediateSymbolDecoder::new(a, hdpc, symbols, num_symbols, false);
|
||||
decoder.execute();
|
||||
assert!(
|
||||
(decoder.get_symbol_mul_ops() as f64 / num_symbols as f64) < expected_mul_ops,
|
||||
|
Loading…
Reference in New Issue
Block a user