hyperliquid-dex / hyperliquid-rust-sdk

MIT License
57 stars 47 forks source link

Improve cloid parsing performance #14

Closed dennohpeter closed 3 months ago

dennohpeter commented 7 months ago

Conversion of cloid from Uuid to hex string can be improved by ~2.3x. This PR does some internal optimization on the conversion by using uuid.simple() function to represent the the random generated value. It also makes use of serde serialize_with variant to ease on serializing cloid without having to import the utility helper function allover the codebase

Benches

// Current implementation

Benchmarking uuid v4
Benchmarking uuid v4: Warming up for 3.0000 s
Benchmarking uuid v4: Collecting 100 samples in estimated 5.0071 s (2388650 iterations)
Benchmarking uuid v4: Analyzing
uuid v4                 time:   [2.0797 µs 2.0876 µs 2.0954 µs]
                        change: [+0.1580% +0.6380% +1.1368%] (p = 0.01 < 0.05)
                        Change within noise threshold.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high severe
slope  [2.0797 µs 2.0954 µs] R^2            [0.9651618 0.9652152]
mean   [2.0785 µs 2.0915 µs] std. dev.      [27.607 ns 40.760 ns]
median [2.0717 µs 2.0904 µs] med. abs. dev. [22.321 ns 39.474 ns]
// New implementation
Benchmarking uuid v4 simple
Benchmarking uuid v4 simple: Warming up for 3.0000 s
Benchmarking uuid v4 simple: Collecting 100 samples in estimated 5.0023 s (5494400 iterations)
Benchmarking uuid v4 simple: Analyzing
uuid v4 simple          time:   [916.89 ns 922.67 ns 928.48 ns]
slope  [916.89 ns 928.48 ns] R^2            [0.9310848 0.9310264]
mean   [927.75 ns 936.43 ns] std. dev.      [19.434 ns 24.910 ns]
median [929.27 ns 940.77 ns] med. abs. dev. [12.886 ns 24.725 ns]

Samples to Reproduce

// Current
#[derive(Serialize)]
pub struct ClientOrderRequest {
    pub cloid: Option<String>,
}

#[inline]
pub fn cloid_with_uuid() -> String {
    let uuid = Uuid::new_v4();

    let cloid = uuid_to_hex_string(uuid);

    serde_json::to_value(ClientOrderRequest { cloid: Some(cloid) })
        .unwrap()
        .to_string()
}

pub fn uuid_to_hex_string(uuid: Uuid) -> String {
    let hex_string = uuid
        .as_bytes()
        .iter()
        .map(|byte| format!("{:02x}", byte))
        .collect::<Vec<String>>()
        .join("");
    format!("0x{}", hex_string)
}
// New
#[derive(Serialize)]
pub struct ClientOrderRequest {
    #[serde(serialize_with = "as_hex_option")]
    pub cloid: Option<Uuid>,
}

#[inline]
pub fn cloid_with_uuid_simple() -> String {
    let cloid = Uuid::new_v4();

    serde_json::to_value(ClientOrderRequest { cloid: Some(cloid) })
        .unwrap()
        .to_string()
}

pub fn as_hex_option<S>(cloid: &Option<Uuid>, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    if let Some(cloid) = cloid {
        s.serialize_str(&format!("0x{}", cloid.simple()))
    } else {
        s.serialize_none()
    }
}
dennohpeter commented 5 months ago

@Nayshins @trophies2015 @rajivpo @traderben @adam254689, do you have any comments on this?