Giter Club home page Giter Club logo

bob's Introduction

Benchmarking Builder Pattern (in Rust)

The builder pattern is a creational design pattern used to construct complex objects step by step. It separates the construction of an object from its representation, allowing the same construction process to create different representations.

In Rust, the builder pattern is commonly used to create structs with many optional fields or fields that can have different types. It provides a fluent API for setting values of these fields and eventually constructing the desired object.

Types of Builder Patterns

For all the example in this topic, consider this struct

struct Struct {
   name: String,
}

1. StructBuilder with Owned Types (Mutable Reference)

This builder pattern uses owned types for the fields and propogates them using mutable reference. It allows setting values using mutable references to the builder.

struct StructBuilder {
    name: Option<String>,
}

impl StructBuilder {
    pub fn name(&mut self, name: &str) -> &mut Self {
        self.name = Some(name.to_owned());
        self
    }

    pub fn build(self) -> Struct {
        Struct {
            name: self.name.unwrap_or_default(),
        }
    }
}

2. StructBuilder with Borrowed Types (Mutable Reference)

This builder pattern uses borrowed types for the fields and propogates them using mutable reference. It allows setting values using borrowed references to the builder.

struct StructBuilder<'a> {
    name: Option<&'a str>,
}

impl<'a> StructBuilder<'a> {
    pub fn name(&mut self, name: &'a str) -> &mut Self {
        self.name = Some(name);
        self
    }

    pub fn build(self) -> Struct {
        Struct {
            name: self.name.unwrap_or_default().into(),
        }
    }
}

3. StructBuilder with Owned Types (Owned Type)

This builder pattern uses owned types for the fields and propogates them using owned type. It allows setting values using owned references to the builder.

struct StructBuilder {
    name: Option<String>,
}

impl StructBuilder {
    pub fn name(self, name: &str) -> Self {
        Self {
            name: Some(name.to_owned()),
            ..self
        }
    }

    // build() same as 1
 }

4. StructBuilder with Borrowed Types (Owned Type)

This builder pattern uses borrowed types for the fields and propogates them using owned type. It allows setting values using owned references to the builder.

struct StructBuilder<'a> {
    name: Option<&'a str>,
}

impl<'a> StructBuilder<'a> {
    pub fn name(self, name: &'a str) -> Self {
        Self {
            name: Some(name),
            ..self
        }
    }

    // build() same as 2
 }

Benchmarking

Benchmarks are important, specially when performance is critical to decide which builder pattern to use

My intuitive thought were that 2. StructBuilder with Borrowed Types (Mutable Reference) would be most performant. But to my surprise it was not.

For benchmarking, All 4 types of builder is implemented on the following struct

#[derive(Debug)]
pub struct Cat {
    name: String,
    username: String,
    number: Option<i64>,
    friends: Vec<String>,
}

You can run the benchmarks yourself on this repo with cargo bench

Types of Benchmarks performed

Random Data Builder

The Random Data Builder benchmarks the performance of constructing structs with randomly generated data. This benchmark simulates real-world scenarios where the values of struct fields may vary each time an object is created.

Definite Data Builder

The Definite Data Builder benchmarks the performance of constructing structs with definite data, where the values of struct fields are predetermined. This benchmark provides insights into the overhead introduced by the builder pattern when using fixed values for object creation.

Results

Reading the titles in results:

Title format: rand|def, mutref|owned, brw|owned

  1. rand|def: rand means random data was generated and supplied, def means definite (predetermined) data was provided
  2. mutref|owned: muref means builder was propogated using mutable reference, owned means builder was propogated via owned type
  3. brw|owned: brw means the builder struct contains borrowed types, owned means builder struct contains owned types
randmutrefowned         time:   [449.74 ns 453.26 ns 456.70 ns]
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) low severe
  1 (1.00%) high severe

randmutrefbrw           time:   [541.01 ns 548.86 ns 557.56 ns]
Found 4 outliers among 100 measurements (4.00%)
  4 (4.00%) high mild

randownedowned          time:   [478.31 ns 482.57 ns 487.14 ns]
Found 5 outliers among 100 measurements (5.00%)
  3 (3.00%) low mild
  2 (2.00%) high mild

randownedbrw            time:   [506.05 ns 509.67 ns 513.56 ns]
Found 8 outliers among 100 measurements (8.00%)
  2 (2.00%) low severe
  3 (3.00%) low mild
  3 (3.00%) high mild

defmutrefowned          time:   [164.30 ns 165.26 ns 166.20 ns]
Found 4 outliers among 100 measurements (4.00%)
  4 (4.00%) high mild

defmutrefbrw            time:   [93.536 ns 94.207 ns 94.943 ns]
Found 5 outliers among 100 measurements (5.00%)
  1 (1.00%) low mild
  3 (3.00%) high mild
  1 (1.00%) high severe

defownedowned           time:   [67.587 ns 67.979 ns 68.396 ns]
Found 3 outliers among 100 measurements (3.00%)
  2 (2.00%) low mild
  1 (1.00%) high mild

defownedbrw             time:   [91.504 ns 92.225 ns 93.050 ns]
Found 6 outliers among 100 measurements (6.00%)
  3 (3.00%) high mild
  3 (3.00%) high severe

Inference

Based on the provided benchmark results, we can draw several conclusions regarding the performance of different builder patterns.

Here are the key inferences:

1. Random Data vs. Definite Data:

Builders with definite (predetermined) data consistently showed lower execution times compared to those with random data. This suggests that constructing structs with predetermined data is generally more efficient than generating random data dynamically.

2. Mutable Reference vs. Owned Type Propagation:

Builders propagated using owned types tended to have slightly better performance compared to those using mutable references, regardless of whether the builder struct contained borrowed or owned types. This implies that owned type propagation may offer slightly better performance and cleaner code.

3. Borrowed Types Consideration:

The difference in performance between builder patterns containing borrowed types and those containing owned types was not significant. While builders with borrowed types may have slightly higher execution times, the impact on performance may be acceptable depending on the specific requirements of the application.

4. Outliers:

The presence of outliers, especially high mild outliers, suggests some variability in the benchmark measurements. While most measurements are consistent, outliers may indicate occasional deviations from the norm.

Conclusion

Using Owned type propogation and Owned type in Builder is suggested as per the benchmark

bob's People

Contributors

atamakahere-git avatar

Stargazers

 avatar NiktoX2 avatar Qizheng Wei avatar Wenbo Zhang avatar  avatar akumarujon avatar Serg Alex avatar orzogc avatar  avatar Egor Lynov avatar  avatar Alex avatar Ricky Chon avatar Varsh avatar Mohammad Alif Yasir bin Soleh avatar Karambit avatar Ishan Grover avatar  avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.