2

I've implemented a Rust clone of bgpdump that displays MRT messages in the same format as bgpdump. To print the message, I implemented the Display trait and printed it to stdout. However, I'm concerned that the Display trait is enormously slow compared to Serde Serialize trait when printing the same message in JSON format.

For instance:

  • Serde_json serialize take => 48s to dump 12G of data
  • Display trait take => more than 5mins to dump 5G of data

My benchmark is quite strait forward I dump the same file in the two different format (txt vs json) and I time it as follows :

> time mrtdump --print filename > dump.txt # print use the display trait 
> time mrtdump --json filename > dump.json # use the serde_json 

I don't quite understand why there is such gap in performance between the two approaches.

Here is my implementation of the Display trait

impl Display for RibIpV4Unicast {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for entry in &self.rib_entries {
            writeln!(f, "TIME: {}", self.time.format("%Y-%m-%d %H:%M:%S"))?;
            writeln!(f, "TYPE: TABLE_DUMP_V2/IPV4_UNICAST")?;
            writeln!(f, "PREFIX: {}/{}", self.prefix, self.prefix_len)?;
            writeln!(f, "SEQUENCE: {}", self.sequence_number)?;
            writeln!(f, "FROM: {} AS {}", entry.peer_ip, entry.peer_asn)?;
            writeln!(
                f,
                "ORIGINATED: {}",
                entry.originated_time.format("%Y-%m-%d %H:%M:%S")
            )?;
            if let Some(origin) = &entry.bgp_origin {
                writeln!(f, "ORIGIN: {}", origin)?;
            }
            if let Some(as_path) = &entry.bgp_as_path {
                writeln!(
                    f,
                    "ASPATH: {}",
                    as_path
                        .segments
                        .iter()
                        .map(|seg| seg.to_string())
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            if let Some(next_hop) = &entry.bgp_next_hop {
                writeln!(f, "NEXT_HOP: {}", next_hop.0)?;
            }
            if let Some(multi_exit_disc) = &entry.bgp_multi_exit_disc {
                writeln!(f, "MULTI_EXIT_DISC: {}", multi_exit_disc.0)?;
            }
            if let Some(communities) = &entry.bgp_community {
                writeln!(
                    f,
                    "COMMUNITIES: {}",
                    communities
                        .0
                        .iter()
                        .map(|(asn, local)| format!("{}:{}", asn, local))
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            if let Some(communities) = &entry.bgp_large_community {
                writeln!(
                    f,
                    "LARGE_COMMUNITY: {}",
                    communities
                        .0
                        .iter()
                        .map(|(asn, local, global)| format!("{}:{}:{}", asn, local, global))
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            if let Some(aggregator) = &entry.bgp_aggregator {
                writeln!(f, "AGGREGATOR: {} {}", aggregator.asn, aggregator.ip)?;
            }
            writeln!(f)?;
        }
        Ok(())
    }
}

Which provide the following output

> mrtdump rib.20250701.0000
TIME: 2025-07-01 00:00:00
TYPE: TABLE_DUMP_V2/IPV4_UNICAST
PREFIX: 0.0.0.0/0
SEQUENCE: 0
FROM: 87.121.64.4 AS57463
ORIGINATED: 2025-06-26 21:10:33
ORIGIN: IGP
ASPATH: 57463 3356
NEXT_HOP: 87.121.64.4
COMMUNITIES: 1:1085 64700:3356 65400:1 65400:65500
LARGE_COMMUNITY: 57463:64700:3356
...
6
  • 3
    Two things come to mind: 1) you're creating a lot of intermediate string allocations which would be best avoided if you're after performance. 2) exactly how you timed the difference may be impactful - for example if you timed the Display impl by letting it print to your terminal, your terminal can very easily be the bottleneck and slow things down. So please provide the benchmarking code. Commented Aug 19 at 19:48
  • My benchmark is quite strait forward I dumm the same file in the two different format (txt vs json) and I time it as follows : ``` > time mrtdump filename > dump.txt > time mrtdump --json filename > dump.json ``` Commented Aug 19 at 19:52
  • 3
    Display was not designed for speed, especially throughput, while Serialize was very much designed for speed. So it's not a surprise. Commented Aug 19 at 19:59
  • 1
    Yes use my program compiled with the release flag for my benchmark Commented Aug 19 at 20:00
  • 1
    If the Display is not designed for speed, what other serialization approach or trait shoud I use to provide the same result ? Commented Aug 19 at 20:03

1 Answer 1

3

Start by making sure the comparison is apples to apples. This code doesn't look quite fair to me:

    if arg.json {
        writeln!(writer, "{}", to_string_pretty(&rib_ipv4_unicast)?)?;
    } else if arg.print {
        writeln!(writer, "{}", &rib_ipv4_unicast)?;
    }

The first line first converts the data to a big string of JSON in memory, then writes the whole thing to stdout with a single write system call.

The second line writes to the BufWriter piece by piece, which then writes to stdout in (I think) 4KB or 8KB chunks. This is more system calls. Since the data is many gigabytes, it might be adding up.

You should see the difference if you run both commands under strace to observe the system calls.

You can eliminate that difference by changing the second line to:

        writeln!(writer, "{}", rib_ipv4_unicast.to_string())?;

That said, Display probably is much slower than serde. But start here.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.