I have had a lot of thinking over the past month about apint, and I think I will put most of it here. These are things I had off the top of my head that I had to type down, maybe I will remember more.
Calling BitWidth::new(...).unwrap() just to handle the 0 width case outside the function might seem to have barely any performance increase when dynamically setting a variable with it and then calling several constructors with that one variable, but I think doing many small things like it will make a significant difference. In benchmarks I just did, we are outperforming ramp by about 0.4x on basic ops, and I think it is because of many small branching choices I am making in the new arithmetic.rs and because of all your small design choices working together to make a big difference. For brevity though, I think we should make a macro, perhaps bw!(), and make it a procedural macro so that it produces compilation errors on 0. We could also use the procedural macro crate for future compile time stuff. BitWidth::new() should be kept for dynamic bit size purposes, and I wonder if we should get rid of the other BitWidth constructors since they have the unwrap inside them, and I much prefer to be able to see unwraps and mess with Results. We obviously cannot do the same thing with ShiftAmount but we should keep that, BitPos, and Radix for purposes of easily distinguishing between widths and positions and shifts and them having special purpose methods. Thorough documentation could also be included for each of these types later.
I had some trouble getting around the crate for a long while, and I think making the following organizational changes would help alot. Make sure that all tests pass before doing this, change to edition = 2018, cargo fix it
- Keep the stuff in
uint.rs and int.rs where they are. They are just complicated wrappers around ApInt and almost no impls for them should be anywhere else.
mod.rs should be changed to apint.rs. The extremely unsafe and important stuff like the Drop impls in constructor.rs and the Clone impl in casting.rs should be moved here.
- The debug and hash impls in
apint/utils.rs should be moved to serialization.rs
- The
is_one, is_even, etc in apint/utils.rs should be put in relational.rs
- The outer
utils.rs should be merged with apint/utils.rs
- I would take the impls for
ShiftAmount and what is in checks.rs and put them in a new file shiftamount.rs.
- Then put
shiftamount.rs, bitpos.rs, bitwidth.rs, radix.rs, traits.rs, and error.rs in their own folder, maybe called info.
apint.rs,int.rs, uint.rs, digit.rs, digit_seq.rs are put into a folder called data
arithmetic.rs, casting.rs, constructors.rs, relational.rs, shift.rs,bitwise.rs, and utils.rs are put into a folder called logic
rand_impl.rs, serialization.rs, serde_impl.rs, to_primitive.rs, lib.rs, and the folders are what remain at the top level of src
A while ago I raised an issue about the Clone impl you wrote for apint but benchmarks show that it is performing as quickly as what ramp has, so unless we happened to end up with a same less than optimal cloning routine, it is good.
I just realized something. The impl for ApInt looks like:
/// An arbitrary precision integer with modulo arithmetics similar to machine integers.
pub struct ApInt {
/// The width in bits of this `ApInt`.
len : BitWidth,
/// The actual data (bits) of this `ApInt`.
data: ApIntData
}
union ApIntData {
/// Inline storage (up to 64 bits) for small-space optimization.
inl: Digit,
/// Extern storage (>64 bits) for larger `ApInt`s.
ext: NonNull<Digit>
}
How does this work? wouldn't the ext have to be NonNull<Storage>?
I have had a lot of thinking over the past month about
apint, and I think I will put most of it here. These are things I had off the top of my head that I had to type down, maybe I will remember more.Calling
BitWidth::new(...).unwrap()just to handle the 0 width case outside the function might seem to have barely any performance increase when dynamically setting a variable with it and then calling several constructors with that one variable, but I think doing many small things like it will make a significant difference. In benchmarks I just did, we are outperformingrampby about 0.4x on basic ops, and I think it is because of many small branching choices I am making in the newarithmetic.rsand because of all your small design choices working together to make a big difference. For brevity though, I think we should make a macro, perhapsbw!(), and make it a procedural macro so that it produces compilation errors on 0. We could also use the procedural macro crate for future compile time stuff.BitWidth::new()should be kept for dynamic bit size purposes, and I wonder if we should get rid of the other BitWidth constructors since they have the unwrap inside them, and I much prefer to be able to seeunwraps and mess withResults. We obviously cannot do the same thing withShiftAmountbut we should keep that,BitPos, andRadixfor purposes of easily distinguishing between widths and positions and shifts and them having special purpose methods. Thorough documentation could also be included for each of these types later.I had some trouble getting around the crate for a long while, and I think making the following organizational changes would help alot. Make sure that all tests pass before doing this, change to
edition = 2018,cargo fixituint.rsandint.rswhere they are. They are just complicated wrappers aroundApIntand almost no impls for them should be anywhere else.mod.rsshould be changed toapint.rs. The extremely unsafe and important stuff like the Drop impls inconstructor.rsand the Clone impl incasting.rsshould be moved here.apint/utils.rsshould be moved toserialization.rsis_one,is_even, etc inapint/utils.rsshould be put inrelational.rsutils.rsshould be merged withapint/utils.rsShiftAmountand what is inchecks.rsand put them in a new fileshiftamount.rs.shiftamount.rs,bitpos.rs,bitwidth.rs,radix.rs,traits.rs, anderror.rsin their own folder, maybe calledinfo.apint.rs,int.rs,uint.rs,digit.rs,digit_seq.rsare put into a folder calleddataarithmetic.rs,casting.rs,constructors.rs,relational.rs,shift.rs,bitwise.rs, andutils.rsare put into a folder calledlogicrand_impl.rs,serialization.rs,serde_impl.rs,to_primitive.rs,lib.rs, and the folders are what remain at the top level ofsrcA while ago I raised an issue about the Clone impl you wrote for
apintbut benchmarks show that it is performing as quickly as whatramphas, so unless we happened to end up with a same less than optimal cloning routine, it is good.I just realized something. The impl for ApInt looks like:
How does this work? wouldn't the
exthave to beNonNull<Storage>?