Build times are slower that expected when making simple changes in one file (main.rs)

I wanted to try actix, so i followed this simple tutorial.

This is the code in cargo.toml:

[package]
name = "my_actix"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
actix-web = "4.3.1"

This is the code in main.rs:

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
    HttpResponse::Ok().body(req_body)
}

async fn manual_hello() -> impl Responder {
    HttpResponse::Ok().body("Hey there!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(hello)
            .service(echo)
            .route("/hey", web::get().to(manual_hello))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

This is the first building result and it took 1m 01s

First build result
  Compiling libc v0.2.147
   Compiling cfg-if v1.0.0
   Compiling version_check v0.9.4
   Compiling autocfg v1.1.0
   Compiling pin-project-lite v0.2.12
   Compiling log v0.4.20
   Compiling bytes v1.4.0
   Compiling futures-core v0.3.28
   Compiling lock_api v0.4.10
   Compiling memchr v2.5.0
   Compiling itoa v1.0.9
   Compiling parking_lot_core v0.9.8
   Compiling once_cell v1.18.0
   Compiling smallvec v1.11.0
   Compiling scopeguard v1.2.0
   Compiling proc-macro2 v1.0.66
   Compiling jobserver v0.1.26
   Compiling typenum v1.16.0
   Compiling unicode-ident v1.0.11
   Compiling parking_lot v0.12.1
   Compiling cc v1.0.83
   Compiling tracing-core v0.1.31
   Compiling signal-hook-registry v1.4.1
   Compiling socket2 v0.5.3
   Compiling mio v0.8.8
   Compiling generic-array v0.14.7
   Compiling serde v1.0.186
   Compiling pkg-config v0.3.27
   Compiling futures-task v0.3.28
   Compiling tokio v1.32.0
   Compiling tracing v0.1.37
   Compiling zstd-sys v2.0.8+zstd.1.5.5
   Compiling quote v1.0.33
   Compiling getrandom v0.2.10
   Compiling futures-util v0.3.28
   Compiling fnv v1.0.7
   Compiling http v0.2.9
   Compiling aho-corasick v1.0.4
   Compiling pin-utils v0.1.0
   Compiling paste v1.0.14
   Compiling regex-syntax v0.7.4
   Compiling futures-sink v0.3.28
   Compiling syn v1.0.109
   Compiling percent-encoding v2.3.0
   Compiling regex-automata v0.3.6
   Compiling bytestring v1.3.0
   Compiling indexmap v1.9.3
   Compiling slab v0.4.9
   Compiling tinyvec_macros v0.1.1
   Compiling zstd-safe v6.0.6
   Compiling local-waker v0.1.3
   Compiling crc32fast v1.3.2
   Compiling alloc-no-stdlib v2.0.4
   Compiling alloc-stdlib v0.2.2
   Compiling regex v1.9.3
   Compiling tinyvec v1.6.0
   Compiling tokio-util v0.7.8
   Compiling crypto-common v0.1.6
   Compiling block-buffer v0.10.4
   Compiling rand_core v0.6.4
   Compiling ahash v0.8.3
   Compiling hashbrown v0.12.3
   Compiling adler v1.0.2
   Compiling ppv-lite86 v0.2.17
   Compiling httparse v1.8.0
   Compiling time-core v0.1.1
   Compiling time-macros v0.2.13
   Compiling rand_chacha v0.3.1
   Compiling miniz_oxide v0.7.1
   Compiling digest v0.10.7
   Compiling actix-service v2.0.2
   Compiling unicode-normalization v0.1.22
   Compiling brotli-decompressor v2.3.4
   Compiling actix-utils v3.0.1
   Compiling form_urlencoded v1.2.0
   Compiling actix-rt v2.8.0
   Compiling ahash v0.7.6
   Compiling cookie v0.16.2
   Compiling cpufeatures v0.2.9
   Compiling serde_json v1.0.105
   Compiling unicode-bidi v0.3.13
   Compiling convert_case v0.4.0
   Compiling deranged v0.3.8
   Compiling ryu v1.0.15
   Compiling bitflags v1.3.2
   Compiling derive_more v0.99.17
   Compiling actix-codec v0.5.1
   Compiling idna v0.4.0
   Compiling brotli v3.3.4
   Compiling time v0.3.27
   Compiling sha1 v0.10.5
   Compiling h2 v0.3.21
   Compiling flate2 v1.0.27
   Compiling rand v0.8.5
   Compiling actix-router v0.5.1
   Compiling local-channel v0.1.3
   Compiling syn v2.0.29
   Compiling num_cpus v1.16.0
   Compiling socket2 v0.4.9
   Compiling encoding_rs v0.8.33
   Compiling httpdate v1.0.3
   Compiling language-tags v0.3.2
   Compiling mime v0.3.17
   Compiling base64 v0.21.2
   Compiling actix-server v2.2.0
   Compiling actix-web-codegen v4.2.0
   Compiling actix-macros v0.2.4
   Compiling url v2.4.0
   Compiling serde_urlencoded v0.7.1
   Compiling zstd v0.12.4
   Compiling actix-http v3.3.1
   Compiling actix-web v4.3.1
   Compiling my_actix v0.1.0 (/home/user0/Projects/rust/tests/actix)
    Finished dev [unoptimized + debuginfo] target(s) in 1m 01s
     Running `target/debug/my_actix`

Now when i build again without changing anything, it takes 0.08s, this is normal.

But when i make a small change in the main.rs file, let's say from:

HttpResponse::Ok().body("Hello world!")

To:

HttpResponse::Ok().body("Hello Man!")

I only changed "Hello world!" to "Hello Man!"

Now it takes nearly 4.23s to build the program:

 Compiling my_actix v0.1.0 (/home/user0/Projects/rust/tests/actix)
    Finished dev [unoptimized + debuginfo] target(s) in 4.60s
     Running `target/debug/my_actix`

Why does it take so long (4.23s) to build the package although i just changed one line in one file (main.rs) ?

My system info
Operating System: Fedora Linux 38
KDE Plasma Version: 5.27.6
KDE Frameworks Version: 5.108.0
Qt Version: 5.15.10
Kernel Version: 6.4.11-200.fc38.x86_64 (64-bit)
Graphics Platform: Wayland
Processors: 4 × Intel® Core™ i5-7200U CPU @ 2.50GHz
Memory: 23.1 GiB of RAM

You are probably rebuilding the whole binary. Use cargo check instead.

This is a web server, so i have to run the program to see results and work on the app, i cannot just check if there are errors and that's it.

This is the result of the last build mentioned above after making the simple change to the string:

Build output
       Fresh cfg-if v1.0.0
       Fresh version_check v0.9.4
       Fresh autocfg v1.1.0
       Fresh libc v0.2.147
       Fresh pin-project-lite v0.2.12
       Fresh log v0.4.20
       Fresh bytes v1.4.0
       Fresh once_cell v1.18.0
       Fresh itoa v1.0.9
       Fresh futures-core v0.3.28
       Fresh smallvec v1.11.0
       Fresh scopeguard v1.2.0
       Fresh memchr v2.5.0
       Fresh lock_api v0.4.10
       Fresh parking_lot_core v0.9.8
       Fresh jobserver v0.1.26
       Fresh unicode-ident v1.0.11
       Fresh cc v1.0.83
       Fresh parking_lot v0.12.1
       Fresh proc-macro2 v1.0.66
       Fresh signal-hook-registry v1.4.1
       Fresh mio v0.8.8
       Fresh socket2 v0.5.3
       Fresh pkg-config v0.3.27
       Fresh tokio v1.32.0
       Fresh typenum v1.16.0
       Fresh quote v1.0.33
       Fresh getrandom v0.2.10
       Fresh fnv v1.0.7
       Fresh http v0.2.9
       Fresh generic-array v0.14.7
       Fresh serde v1.0.186
       Fresh futures-task v0.3.28
       Fresh aho-corasick v1.0.4
       Fresh futures-sink v0.3.28
       Fresh pin-utils v0.1.0
       Fresh regex-syntax v0.7.4
       Fresh percent-encoding v2.3.0
       Fresh regex-automata v0.3.6
       Fresh futures-util v0.3.28
       Fresh bytestring v1.3.0
       Fresh local-waker v0.1.3
       Fresh tinyvec_macros v0.1.1
       Fresh alloc-no-stdlib v2.0.4
       Fresh tinyvec v1.6.0
       Fresh alloc-stdlib v0.2.2
       Fresh paste v1.0.14
       Fresh syn v1.0.109
       Fresh regex v1.9.3
       Fresh zstd-sys v2.0.8+zstd.1.5.5
       Fresh tokio-util v0.7.8
       Fresh block-buffer v0.10.4
       Fresh crypto-common v0.1.6
       Fresh rand_core v0.6.4
       Fresh hashbrown v0.12.3
       Fresh time-core v0.1.1
       Fresh ppv-lite86 v0.2.17
       Fresh tracing-core v0.1.31
       Fresh adler v1.0.2
       Fresh tracing v0.1.37
       Fresh indexmap v1.9.3
       Fresh miniz_oxide v0.7.1
       Fresh rand_chacha v0.3.1
       Fresh time-macros v0.2.13
       Fresh digest v0.10.7
       Fresh zstd-safe v6.0.6
       Fresh slab v0.4.9
       Fresh actix-service v2.0.2
       Fresh brotli-decompressor v2.3.4
       Fresh unicode-normalization v0.1.22
       Fresh crc32fast v1.3.2
       Fresh actix-utils v3.0.1
       Fresh form_urlencoded v1.2.0
       Fresh actix-rt v2.8.0
       Fresh unicode-bidi v0.3.13
       Fresh ryu v1.0.15
       Fresh cpufeatures v0.2.9
       Fresh deranged v0.3.8
       Fresh bitflags v1.3.2
       Fresh convert_case v0.4.0
       Fresh time v0.3.27
       Fresh actix-codec v0.5.1
       Fresh sha1 v0.10.5
       Fresh derive_more v0.99.17
       Fresh idna v0.4.0
       Fresh flate2 v1.0.27
       Fresh brotli v3.3.4
       Fresh ahash v0.8.3
       Fresh h2 v0.3.21
       Fresh zstd v0.12.4
       Fresh httparse v1.8.0
       Fresh rand v0.8.5
       Fresh local-channel v0.1.3
       Fresh syn v2.0.29
       Fresh socket2 v0.4.9
       Fresh num_cpus v1.16.0
       Fresh encoding_rs v0.8.33
       Fresh httpdate v1.0.3
       Fresh language-tags v0.3.2
       Fresh base64 v0.21.2
       Fresh mime v0.3.17
       Fresh actix-macros v0.2.4
       Fresh actix-server v2.2.0
       Fresh actix-web-codegen v4.2.0
       Fresh actix-http v3.3.1
       Fresh url v2.4.0
       Fresh serde_json v1.0.105
       Fresh ahash v0.7.6
       Fresh cookie v0.16.2
       Fresh serde_urlencoded v0.7.1
       Fresh actix-router v0.5.1
       Fresh actix-web v4.3.1
       Dirty my_actix v0.1.0 (/home/user0/Projects/rust/tests/actix): the file `src/main.rs` has changed (1692907665.366504518s, 26m 59s after last build at 1692906046.407834612s)
   Compiling my_actix v0.1.0 (/home/user0/Projects/rust/tests/actix)
     Running `/home/user0/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name my_actix --edition=2021 src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=167 --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C metadata=f3ea4653ef1f2d6d -C extra-filename=-f3ea4653ef1f2d6d --out-dir /home/user0/Projects/rust/tests/actix/target/debug/deps -C incremental=/home/user0/Projects/rust/tests/actix/target/debug/incremental -L dependency=/home/user0/Projects/rust/tests/actix/target/debug/deps --extern actix_web=/home/user0/Projects/rust/tests/actix/target/debug/deps/libactix_web-cedb8cfd76e36b69.rlib -L native=/home/user0/Projects/rust/tests/actix/target/debug/build/zstd-sys-1983bc84cb8dd46f/out`
    Finished dev [unoptimized + debuginfo] target(s) in 4.29s
     Running `target/debug/my_actix`

Tho i still cannot understand why it took so long 4.29s

Ah, gotcha. I thought you were just checking for compilation errors.

In that case then yeah, those times are expected. Actix-web is a big web framework with tons of dependencies, and recompiling it takes some time.

I have compiled all the dependencies once, i don't need to compile them again, all i did was change one line in main.rs as i mentioned above ?!

Can you try RUSTC_BOOTSTRAP=1 cargo rustc -- -Ztime-passes to see where it is slow?

2 Likes

This is the output of RUSTC_BOOTSTRAP=1 cargo rustc -- -Ztime-passes

Output
   Compiling my_actix v0.1.0 (/home/user0/Projects/rust/tests/actix)
time:   0.000; rss:   35MB ->   37MB (   +2MB)  parse_crate
time:   0.005; rss:   37MB ->   38MB (   +0MB)  incr_comp_prepare_session_directory
time:   0.001; rss:   38MB ->   39MB (   +1MB)  incr_comp_garbage_collect_session_directories
time:   0.009; rss:   39MB ->   39MB (   +0MB)  blocked_on_dep_graph_loading
time:   0.000; rss:   39MB ->   40MB (   +1MB)  setup_global_ctxt
time:   0.000; rss:   42MB ->   42MB (   +1MB)  crate_injection
time:   0.021; rss:   42MB ->   89MB (  +47MB)  expand_crate
time:   0.021; rss:   42MB ->   89MB (  +47MB)  macro_expand_crate
time:   0.000; rss:   89MB ->   89MB (   +0MB)  AST_validation
time:   0.000; rss:   89MB ->   90MB (   +1MB)  finalize_imports
time:   0.000; rss:   90MB ->   90MB (   +0MB)  compute_effective_visibilities
time:   0.001; rss:   90MB ->   94MB (   +4MB)  late_resolve_crate
time:   0.000; rss:   94MB ->   94MB (   +0MB)  resolve_postprocess
time:   0.002; rss:   89MB ->   94MB (   +6MB)  resolve_crate
time:   0.000; rss:   94MB ->   95MB (   +1MB)  prepare_outputs
time:   0.000; rss:   95MB ->   95MB (   +0MB)  complete_gated_feature_checking
time:   0.001; rss:   95MB ->   97MB (   +2MB)  looking_for_entry_point
time:   0.001; rss:   95MB ->   98MB (   +3MB)  misc_checking_1
time:   0.001; rss:   98MB ->  104MB (   +6MB)  type_collecting
time:   0.000; rss:  104MB ->  104MB (   +0MB)  impl_wf_inference
time:   0.000; rss:  104MB ->  104MB (   +0MB)  coherence_checking
time:   0.000; rss:  104MB ->  106MB (   +2MB)  wf_checking
time:   0.012; rss:  106MB ->  131MB (  +24MB)  item_types_checking
time:   0.032; rss:   98MB ->  137MB (  +39MB)  type_check_crate
time:   0.000; rss:  141MB ->  141MB (   +0MB)  FunctionItemReferences
time:   0.017; rss:  137MB ->  149MB (  +11MB)  MIR_borrow_checking
time:   0.001; rss:  149MB ->  149MB (   +1MB)  ElaborateDrops
time:   0.000; rss:  149MB ->  150MB (   +0MB)  StateTransform
time:   0.001; rss:  150MB ->  150MB (   +0MB)  ConstProp
time:   0.000; rss:  150MB ->  150MB (   +0MB)  LowerIntrinsics
time:   0.000; rss:  150MB ->  151MB (   +1MB)  StateTransform
time:   0.002; rss:  150MB ->  152MB (   +2MB)  ConstProp
time:   0.001; rss:  152MB ->  152MB (   +0MB)  ConstProp
time:   0.001; rss:  152MB ->  152MB (   +0MB)  ElaborateDrops
time:   0.001; rss:  152MB ->  152MB (   +0MB)  StateTransform
time:   0.005; rss:  152MB ->  152MB (   +1MB)  ConstProp
time:   0.000; rss:  152MB ->  153MB (   +0MB)  ConstProp
time:   0.016; rss:  149MB ->  153MB (   +4MB)  MIR_effect_checking
time:   0.001; rss:  153MB ->  153MB (   +0MB)  module_lints
time:   0.001; rss:  153MB ->  153MB (   +0MB)  lint_checking
time:   0.002; rss:  153MB ->  153MB (   +0MB)  misc_checking_3
time:   0.000; rss:  153MB ->  153MB (   +0MB)  monomorphization_collector_root_collections
time:   0.407; rss:  153MB ->  237MB (  +84MB)  monomorphization_collector_graph_walk
time:   0.038; rss:  237MB ->  240MB (   +3MB)  partition_and_assert_distinct_symbols
time:   0.000; rss:  241MB ->  242MB (   +1MB)  write_allocator_module
time:   1.703; rss:  242MB ->  289MB (  +47MB)  codegen_to_LLVM_IR
time:   2.612; rss:  153MB ->  289MB ( +136MB)  codegen_crate
time:   0.000; rss:  289MB ->  289MB (   +0MB)  encode_query_results_for(type_of)
time:   0.000; rss:  290MB ->  290MB (   +0MB)  encode_query_results_for(optimized_mir)
time:   0.000; rss:  290MB ->  290MB (   +0MB)  encode_query_results_for(super_predicates_of)
time:   0.000; rss:  290MB ->  290MB (   +0MB)  encode_query_results_for(thir_check_unsafety)
time:   0.000; rss:  290MB ->  290MB (   +0MB)  encode_query_results_for(coerce_unsized_info)
time:   0.001; rss:  290MB ->  290MB (   +0MB)  encode_query_results_for(eval_to_allocation_raw)
time:   0.000; rss:  290MB ->  291MB (   +0MB)  encode_query_results_for(eval_to_const_value_raw)
time:   0.002; rss:  291MB ->  292MB (   +2MB)  encode_query_results_for(symbol_name)
time:   0.000; rss:  292MB ->  292MB (   +0MB)  encode_query_results_for(is_mir_available)
time:   0.001; rss:  292MB ->  292MB (   +0MB)  encode_query_results_for(codegen_select_candidate)
time:   0.005; rss:  292MB ->  292MB (   +0MB)  encode_query_results_for(specialization_graph_of)
time:   0.000; rss:  293MB ->  293MB (   +0MB)  encode_query_results_for(unused_generic_params)
time:   0.011; rss:  289MB ->  293MB (   +4MB)  encode_query_results
time:   0.013; rss:  289MB ->  293MB (   +4MB)  incr_comp_serialize_result_cache
time:   0.013; rss:  289MB ->  293MB (   +4MB)  incr_comp_persist_result_cache
time:   0.000; rss:  293MB ->  294MB (   +0MB)  incr_comp_persist_dep_graph
time:   0.013; rss:  289MB ->  294MB (   +5MB)  serialize_dep_graph
time:   2.063; rss:  262MB ->  183MB (  -79MB)  LLVM_passes
time:   0.012; rss:  294MB ->  177MB ( -117MB)  free_global_ctxt
time:   3.458; rss:  177MB ->  177MB (   +0MB)  run_linker
time:   3.469; rss:  177MB ->  171MB (   -5MB)  link_binary
time:   3.470; rss:  177MB ->  171MB (   -5MB)  link_crate
time:   3.479; rss:  177MB ->  171MB (   -5MB)  link
time:   6.242; rss:   27MB ->   91MB (  +64MB)  total
    Finished dev [unoptimized + debuginfo] target(s) in 6.35s

I think most time is spent in these:

time:   1.703; rss:  242MB ->  289MB (  +47MB)  codegen_to_LLVM_IR
time:   2.612; rss:  153MB ->  289MB ( +136MB)  codegen_crate
time:   2.063; rss:  262MB ->  183MB (  -79MB)  LLVM_passes
time:   3.458; rss:  177MB ->  177MB (   +0MB)  run_linker
time:   3.469; rss:  177MB ->  171MB (   -5MB)  link_binary
time:   3.470; rss:  177MB ->  171MB (   -5MB)  link_crate
time:   3.479; rss:  177MB ->  171MB (   -5MB)  link

So a bit over half the time is spent in linking. That is likely caused by the size of all your dependencies. You could try using a faster linker like lld or mold. For example for lld you can pass -Clink-arg=-fuse-ld=lld to rustc if you have lld installed already. And mold has a wrapper program which intercepts linker invocations and replaces them with mold invocations.

Here's what I got. These are rebuild times after changing a string.

        debug       release
actix   0m2.930s    0m2.723s
axum    0m1.817s    0m2.036s

For this simple case, axum (port below) rebuilds faster on my machine, especially the debug build.

use axum::{
    routing::{get, post},
    Router,
};

async fn hello() -> &'static str {
    "Hello world!!!"
}

async fn echo(req_body: String) -> String {
    req_body
}

async fn manual_hello() -> &'static str {
    "Hey there!"
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(hello))
        .route("/echo", post(echo))
        .route("/hey", get(manual_hello));
    axum::Server::bind(&"0.0.0.0:8080".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

I also took a look at this topic and after adding this code:

[build]
rustflags = ["-Clink-args=-fuse-ld=lld"]

globally to: /home/user0/.cargo/config.toml

I get total build time of 1.51s instead of 4.29s

3 Likes