Skip to content

Conversation

@Valian
Copy link
Contributor

@Valian Valian commented Jul 15, 2025

Hi 👋 Thanks for an amazing library. While working on LiveVue patches, I've spotted some low-hanging fruits about performance of this package. So here I go 😄

Approach

First, I've created a small benchmark in test/benchmark.exs using Benchee (I've added it to mix.exs, only in dev). Then I've developed a new version of diff alongside existing one, and after being done I've replaced original code to create this PR.

You can start benchmark by running mix run test/benchmark.exs, but currently it will only show numbers for existing implementation. To compare it to the old one we'd need to copy an old implementation into a separate module.

Overview of changes

My goal was to reduce the number of memory allocations. Each time I could skip creating a new object, I tried to do so.

It turns out that a dedicated function for making a diff for maps and for lists is the best way, as it allows to write a highly-optimized code that almost entirely avoids memory allocations.

Results

In my tests new implementation is 2-3x faster, and consumes around 2.5 less memory. Log from benchee:

Benchmarking Faster JsonPatch with input Complex Lists - Task Management ...
Benchmarking Faster JsonPatch with input Complex Maps - E-commerce Order ...
Benchmarking Faster JsonPatch with input Deep Nesting - Configuration Tree ...
Benchmarking Faster JsonPatch with input Mixed Maps and Lists - Social Media Post ...
Benchmarking Original JsonPatch with input Complex Lists - Task Management ...
Benchmarking Original JsonPatch with input Complex Maps - E-commerce Order ...
Benchmarking Original JsonPatch with input Deep Nesting - Configuration Tree ...
Benchmarking Original JsonPatch with input Mixed Maps and Lists - Social Media Post ...
Calculating statistics...
Formatting results...

##### With input Complex Lists - Task Management #####
Name                         ips        average  deviation         median         99th %
Faster JsonPatch         93.72 K       10.67 μs    ±34.80%        9.67 μs       22.33 μs
Original JsonPatch       34.20 K       29.24 μs   ±315.50%       25.58 μs       69.88 μs

Comparison: 
Faster JsonPatch         93.72 K
Original JsonPatch       34.20 K - 2.74x slower +18.57 μs

Memory usage statistics:

Name                  Memory usage
Faster JsonPatch           8.27 KB
Original JsonPatch        21.97 KB - 2.66x memory usage +13.70 KB

**All measurements for memory usage were the same**

Reduction count statistics:

Name               Reduction count
Faster JsonPatch            1.10 K
Original JsonPatch          3.00 K - 2.73x reduction count +1.91 K

**All measurements for reduction count were the same**

##### With input Complex Maps - E-commerce Order #####
Name                         ips        average  deviation         median         99th %
Faster JsonPatch        100.00 K       10.00 μs   ±139.63%        8.75 μs       26.58 μs
Original JsonPatch       41.54 K       24.07 μs   ±283.08%       20.92 μs       49.00 μs

Comparison: 
Faster JsonPatch        100.00 K
Original JsonPatch       41.54 K - 2.41x slower +14.07 μs

Memory usage statistics:

Name                  Memory usage
Faster JsonPatch           7.34 KB
Original JsonPatch        16.30 KB - 2.22x memory usage +8.95 KB

**All measurements for memory usage were the same**

Reduction count statistics:

Name               Reduction count
Faster JsonPatch            0.98 K
Original JsonPatch          2.34 K - 2.39x reduction count +1.36 K

**All measurements for reduction count were the same**

##### With input Deep Nesting - Configuration Tree #####
Name                         ips        average  deviation         median         99th %
Faster JsonPatch         60.95 K       16.41 μs    ±26.29%       15.33 μs       28.21 μs
Original JsonPatch       24.94 K       40.10 μs    ±23.62%       38.13 μs       67.61 μs

Comparison: 
Faster JsonPatch         60.95 K
Original JsonPatch       24.94 K - 2.44x slower +23.69 μs

Memory usage statistics:

Name                  Memory usage
Faster JsonPatch          13.27 KB
Original JsonPatch        36.13 KB - 2.72x memory usage +22.86 KB

**All measurements for memory usage were the same**

Reduction count statistics:

Name               Reduction count
Faster JsonPatch            1.77 K
Original JsonPatch          4.70 K - 2.65x reduction count +2.93 K

**All measurements for reduction count were the same**

All tests are passing, so I have a pretty high confidence everything is working in the same way as before. Hope these changes will be useful!

@Valian
Copy link
Contributor Author

Valian commented Jul 15, 2025

OK found another nice improvement of 50%. It turns out checking if string exists before doing replacement is greatly beneficial for performance. Take a look:

##### With input Complex Lists - Task Management #####
Name                       ips        average  deviation         median         99th %
Faster JsonPatch      144.46 K        6.92 μs    ±53.56%        6.33 μs       21.54 μs
JsonPatch              42.24 K       23.67 μs    ±13.84%       23.50 μs       33.18 μs

Comparison: 
Faster JsonPatch      144.46 K
JsonPatch              42.24 K - 3.42x slower +16.75 μs

##### With input Complex Maps - E-commerce Order #####
Name                       ips        average  deviation         median         99th %
Faster JsonPatch      150.49 K        6.65 μs    ±67.21%        5.75 μs       19.29 μs
JsonPatch              46.75 K       21.39 μs    ±37.90%       20.46 μs       36.01 μs

Comparison: 
Faster JsonPatch      150.49 K
JsonPatch              46.75 K - 3.22x slower +14.74 μs

##### With input Deep Nesting - Configuration Tree #####
Name                       ips        average  deviation         median         99th %
Faster JsonPatch       84.30 K       11.86 μs    ±33.07%       10.58 μs       25.75 μs
JsonPatch              25.54 K       39.15 μs    ±13.86%       37.42 μs       57.54 μs

Comparison: 
Faster JsonPatch       84.30 K
JsonPatch              25.54 K - 3.30x slower +27.29 μs

##### With input Mixed Maps and Lists - Social Media Post #####
Name                       ips        average  deviation         median         99th %
Faster JsonPatch      124.02 K        8.06 μs    ±40.10%        7.08 μs       20.33 μs
JsonPatch              38.22 K       26.16 μs    ±12.99%       25.92 μs       37.50 μs

Comparison: 
Faster JsonPatch      124.02 K
JsonPatch              38.22 K - 3.24x slower +18.10 μs

@corka149 corka149 self-assigned this Jul 15, 2025
@corka149
Copy link
Owner

Hi @Valian thanks for all the effort. <3

I will have a look at it asap.

@corka149 corka149 self-requested a review July 16, 2025 15:55
corka149
corka149 previously approved these changes Jul 16, 2025
Copy link
Owner

@corka149 corka149 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me - please fix just the credo issues.

@corka149 corka149 added this to the 2.3 milestone Jul 18, 2025
@Valian
Copy link
Contributor Author

Valian commented Jul 18, 2025

@corka149 Fixed! 👍🏻

@corka149 corka149 force-pushed the diff-performance-optimization branch from 1760247 to d04a22c Compare July 19, 2025 07:18
@corka149 corka149 merged commit 46fc20d into corka149:master Jul 20, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants