Advertisement
burjui

Bitfield benchmark

Dec 29th, 2024 (edited)
505
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 6.03 KB | Source Code | 0 0
  1. #![allow(
  2.     clippy::cast_sign_loss,
  3.     clippy::cast_possible_truncation,
  4.     clippy::cast_lossless
  5. )]
  6.  
  7. use std::{ops::RangeInclusive, time::Instant};
  8.  
  9. use bitvec::order::Lsb0;
  10. use bitvec::view::BitView;
  11. use rand::{thread_rng, Rng};
  12.  
  13. fn main() {
  14.     benchmark();
  15. }
  16.  
  17. fn benchmark() {
  18.     let gen_signed =
  19.         |nbits: usize| -> i32 { thread_rng().gen_range(-(1 << (nbits - 1))..1 << (nbits - 1)) };
  20.     let gen_unsigned = |nbits: usize| -> u32 { thread_rng().gen_range(0..1 << nbits) };
  21.     let data = (0..1_000_000)
  22.         .map(|_| {
  23.             (
  24.                 gen_unsigned(7) as u8,
  25.                 gen_signed(13) as i16,
  26.                 gen_unsigned(3) as u8,
  27.                 gen_unsigned(5) as u8,
  28.                 gen_unsigned(5) as u8,
  29.             )
  30.         })
  31.         .collect::<Vec<_>>();
  32.  
  33.     let mut merge_results = vec![0; data.len()];
  34.     let start = Instant::now();
  35.     for (i, (opcode, imm, funct3, rs1, rs2)) in data.iter().copied().enumerate() {
  36.         merge_results[i] = riscv_b_instruction_merge(opcode, imm, funct3, rs1, rs2);
  37.     }
  38.     let end = Instant::now();
  39.     println!("merge: {:?}", end - start);
  40.  
  41.     let mut bitvec_results = vec![0; data.len()];
  42.     let start = Instant::now();
  43.     for (i, (opcode, imm, funct3, rs1, rs2)) in data.iter().copied().enumerate() {
  44.         bitvec_results[i] = riscv_b_instruction_bitvec(opcode, imm, funct3, rs1, rs2);
  45.     }
  46.     let end = Instant::now();
  47.     println!("bitvec: {:?}", end - start);
  48.  
  49.     let mut manual_results = vec![0; data.len()];
  50.     let start = Instant::now();
  51.     for (i, (opcode, imm, funct3, rs1, rs2)) in data.iter().copied().enumerate() {
  52.         manual_results[i] = riscv_b_instruction_manual(opcode, imm, funct3, rs1, rs2);
  53.     }
  54.     let end = Instant::now();
  55.     println!("manual: {:?}", end - start);
  56.  
  57.     for ((merge_result, bitvec_result), manual_result) in merge_results
  58.         .into_iter()
  59.         .zip(bitvec_results.into_iter())
  60.         .zip(manual_results.into_iter())
  61.     {
  62.         assert!(
  63.             merge_result == bitvec_result,
  64.             "\nmerge\t{:032b}\nbitvec\t{:032b}\ndiff\t{}\n",
  65.             merge_result,
  66.             bitvec_result,
  67.             diff(merge_result, bitvec_result)
  68.         );
  69.         assert!(
  70.             merge_result == manual_result,
  71.             "\nmerge\t{:032b}\nmanual\t{:032b}\ndiff\t{}\n",
  72.             merge_result,
  73.             manual_result,
  74.             diff(merge_result, manual_result)
  75.         );
  76.     }
  77. }
  78.  
  79. fn diff(a: u32, b: u32) -> String {
  80.     let diff = a ^ b;
  81.     let mut result = String::with_capacity(32);
  82.     for i in 0..32 {
  83.         let index = (diff >> (31 - i)) & 1;
  84.         result.push(['_', '^'][index as usize]);
  85.     }
  86.     result
  87. }
  88.  
  89. // #[inline(always)]
  90. #[allow(clippy::inline_always)]
  91. fn riscv_b_instruction_merge(opcode: u8, imm: i16, funct3: u8, rs1: u8, rs2: u8) -> u32 {
  92.     merge_bitfields([
  93.         (0..=6, opcode as u32, 0..=6),
  94.         (7..=7, imm as u32, 11..=11),
  95.         (8..=11, imm as u32, 1..=4),
  96.         (12..=14, funct3 as u32, 0..=2),
  97.         (15..=19, rs1 as u32, 0..=4),
  98.         (20..=24, rs2 as u32, 0..=4),
  99.         (25..=30, imm as u32, 5..=10),
  100.         (31..=31, imm as u32, 12..=12),
  101.     ])
  102. }
  103.  
  104. // #[inline(always)]
  105. #[allow(clippy::inline_always)]
  106. fn riscv_b_instruction_manual(opcode: u8, imm: i16, funct3: u8, rs1: u8, rs2: u8) -> u32 {
  107.     (opcode as u32) & (0b0111_1111)
  108.         | ((((imm >> 11) as u32) & 1) << 7)
  109.         | ((((imm >> 1) as u32) & 0b1111) << 8)
  110.         | ((funct3 as u32 & 0b111) << 12)
  111.         | ((rs1 as u32 & 0b11111) << 15)
  112.         | ((rs2 as u32 & 0b11111) << 20)
  113.         | ((((imm >> 5) as u32) & 0b11_1111) << 25)
  114.         | (((((imm >> 12) as u32) >> 12) & 1) << 31)
  115. }
  116.  
  117. // #[inline(always)]
  118. #[allow(clippy::inline_always)]
  119. fn riscv_b_instruction_bitvec(opcode: u8, imm: i16, funct3: u8, rs1: u8, rs2: u8) -> u32 {
  120.     let opcode = opcode as u32;
  121.     let opcode = opcode.view_bits::<Lsb0>();
  122.     let imm = imm as u32;
  123.     let imm = imm.view_bits::<Lsb0>();
  124.     let funct3 = funct3 as u32;
  125.     let funct3 = funct3.view_bits::<Lsb0>();
  126.     let rs1 = rs1 as u32;
  127.     let rs1 = rs1.view_bits::<Lsb0>();
  128.     let rs2 = rs2 as u32;
  129.     let rs2 = rs2.view_bits::<Lsb0>();
  130.  
  131.     let mut instruction = 0;
  132.     let bits = instruction.view_bits_mut::<Lsb0>();
  133.     bits[0..7].copy_from_bitslice(&opcode[0..7]);
  134.     bits[7..8].copy_from_bitslice(&imm[11..12]);
  135.     bits[8..12].copy_from_bitslice(&imm[1..5]);
  136.     bits[12..15].copy_from_bitslice(&funct3[0..3]);
  137.     bits[15..20].copy_from_bitslice(&rs1[0..5]);
  138.     bits[20..25].copy_from_bitslice(&rs2[0..5]);
  139.     bits[25..31].copy_from_bitslice(&imm[5..11]);
  140.     bits[31..32].copy_from_bitslice(&imm[12..13]);
  141.     instruction
  142. }
  143.  
  144. /// Combines `bitfields` into a single value. Each bit field is a tuple of:
  145. /// - dst range
  146. /// - value
  147. /// - src range
  148. // FIXME bitfields as array instead of reference
  149. // #[inline(always)]
  150. #[allow(clippy::inline_always)]
  151. pub(crate) const fn merge_bitfields<const N: usize>(
  152.     bitfields: [(RangeInclusive<usize>, u32, RangeInclusive<usize>); N],
  153. ) -> u32 {
  154.     let mut dst_bits_visited = 0u32;
  155.     let mut dst = 0;
  156.     let mut i = 0;
  157.     while i < bitfields.len() {
  158.         let (dst_range, src, src_range) = &bitfields[i];
  159.         assert!(
  160.             *src_range.end() < 32 && *dst_range.end() < 32,
  161.             "bit range crosses 32-bit boundary"
  162.         );
  163.         assert!(
  164.             (*dst_range.end() - *dst_range.start()) == *src_range.end() - *src_range.start(),
  165.             "bit range lengths do not match"
  166.         );
  167.  
  168.         // Copy the bitfield
  169.         dst |= ((*src & !(0xFFFF_FFFF_u32 << *src_range.end() << 1)) >> *src_range.start())
  170.             << *dst_range.start();
  171.  
  172.         // Check for overlaps
  173.         let dst_mask =
  174.             (0xFFFF_FFFF_u32 << *dst_range.end() << 1) ^ (0xFFFF_FFFF_u32 << *dst_range.start());
  175.         assert!(
  176.             dst_bits_visited & dst_mask == 0,
  177.             "bit field overlap detected",
  178.         );
  179.         dst_bits_visited |= dst_mask;
  180.  
  181.         i += 1;
  182.     }
  183.     dst
  184. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement