Use Cargo Workspaces in generated project to reduce compilation times
Currently, running the build scripts takes an unnecessary amount of time because all the scripts are treated like separate binaries but have the same dependencies as the backend, meaning that all the dependencies of the backend are compiled twice if you, for example, run cargo fullstack.
https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
For example, running cargo backend will compile every dependency (upwards of 400-500 crates) once, execute the backend.rs binary, which will then compile every dependency again to run the server when it runs cargo watch.
Despite the fact that backend.rs has no dependencies.
Do you mind timing this on a fresh project with and without this workspace change? Also, could you share the output you get in your terminal? I'm not sure I understand the problem
oh sure, I'll get on that
Methodology notes
- Command used to generate project:
cargo run --bin create-rust-app -- create -c -d postgres -b actix-web test-project - ran
cargo cleanbetween each test, not doing so does speed up some compilations because build-artifacts are reused by cargo (but not cargo-watch) - used
time cargo build --bin <binary>to measure runtime - for the "with workspaces" tests: edited/added manifests (included at end of this comment), moved files around, cleaned up dependencies, split dsync.rs and tsync.rs into binary and libary crates, etc.
Key Point:
- all the binaries that run the backend (backend.rs, fullstack.rs) also have to compile it, and cargo-watch doesn't re-use the compilation artifacts from compiling backend/fullstack (whichever was used), this greatly increases the total time spent compiling things
Results
without workspaces
-
backend- Crates compiled: 447
-
timeinformation:cargo build --bin backend 1087.23s user 106.52s system 645% cpu 3:05.07 total
-
dsync- Crates compiled: 447
-
timeinformation:cargo build --bin dsync 1090.79s user 109.00s system 571% cpu 3:30.06 total
-
frontend- Crates compiled: 447
-
timeinformation:cargo build --bin frontend 1052.26s user 106.69s system 607% cpu 3:10.74 total
-
fullstack- Crates compiled: 447
-
timeinformation: these are all compiling the same dependencies, so they're all taking around 3 minutes
-
tsync- Crates compiled: 447
-
timeinformation: these are all compiling the same dependencies, so they're all taking around 3 minutes
- the backend server (
test-project)- Crates compiled: 447
-
timeinformation: these are all compiling the same dependencies, so they're all taking around 3 minutes
with workspaces
-
backend- Crates compiled: 1
-
timeinformation:cargo build --bin backend 1.16s user 0.41s system 105% cpu 1.479 total
-
dsync- Crates compiled: 47 (needs the dsync library)
-
timeinformation:cargo build --bin dsync 105.46s user 11.05s system 465% cpu 25.034 total
-
frontend- Crates compiled: 404 (needs the create-rust-app libary)
-
timeinformation:cargo build --bin frontend 993.41s user 99.09s system 598% cpu 3:02.55 total
-
fullstack- Crates compiled: 422 (needs create-rust-app and some other stuff)
-
timeinformation:cargo build --bin fullstack 992.44s user 101.83s system 579% cpu 3:08.87 total
-
tsync- Crates compiled: 40
-
timeinformation:cargo build --bin tsync 65.34s user 8.26s system 325% cpu 22.612 total
- the backend server (
test-project)- Crates compiled: 417
-
timeinformation:cargo build --bin test-project 958.04s user 94.59s system 534% cpu 3:16.95 total
Manifests
Click to expand
./Cargo.toml (root manifest)
[workspace]
members = [
".cargo/bin/dsync",
".cargo/bin/tsync",
".cargo/bin/fullstack",
".cargo/bin/frontend",
".cargo/bin/backend",
"backend",
]
[workspace.dependencies]
dsync = "0"
tsync = "1"
create-rust-app = { version = "9", default-features = false, features = [
"plugin_dev",
"database_postgres",
"backend_actix-web",
] }
[profile.dev]
debug-assertions = true
./backend/Cargo.toml
[[bin]]
name = "test-project"
path = "src/main.rs"
[dependencies]
actix-files = "0.6.0"
actix-http = "3.0.0"
actix-web = "4.0.1"
futures-util = "0.3.21"
create-rust-app = { workspace = true }
serde_json = "1.0.79"
tsync = { workspace = true }
[dependencies.chrono]
features = ["serde"]
version = "0.4.19"
[dependencies.diesel]
default-features = false
features = ["postgres", "r2d2", "chrono"]
version = "2.0.0-rc.1"
[dependencies.serde]
features = ["derive"]
version = "1.0.133"
[dependencies.tokio]
features = ["full"]
version = "1"
[package]
default-run = "test-project"
edition = "2021"
name = "test-project"
publish = false
version = "0.1.0"
./.cargo/bin/backend/Cargo.toml
[package]
name = "backend"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "backend"
path = "src/backend.rs"
bench = false
test = false
[dependencies]
./.cargo/bin/dsync/Cargo.toml
[package]
name = "dsync"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "dsync"
path = "src/dsync.rs"
bench = false
test = false
[lib]
name = "dsync_lib"
path = "src/lib.rs"
bench = false
test = false
[dependencies]
dsync = {workspace = true}
./.cargo/bin/frontend/Cargo.toml
[package]
name = "frontend"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "frontend"
path = "src/frontend.rs"
bench = false
test = false
[dependencies]
create-rust-app = { workspace = true }
./.cargo/bin/fullstack/Cargo.toml
[package]
name = "fullstack"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "fullstack"
path = "src/fullstack.rs"
bench = false
test = false
[dependencies]
create-rust-app = { workspace = true }
dsync = { path = "../dsync" }
tsync = { path = "../tsync" }
./.cargo/bin/tsync/Cargo.toml
[package]
name = "tsync"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "tsync"
path = "src/tsync.rs"
bench = false
test = false
[lib]
name = "tsync_lib"
path = "src/lib.rs"
bench = false
test = false
[dependencies]
tsync = {workspace = true}
File tree of "using workspaces" test
Click to expand
command used: tree --dirsfirst ./ .cargo
./
├── backend
│ ├── src
│ │ ├── mail
│ │ │ ├── example.rs
│ │ │ └── mod.rs
│ │ ├── models
│ │ │ └── mod.rs
│ │ ├── services
│ │ │ ├── mod.rs
│ │ │ └── todo.rs
│ │ ├── main.rs
│ │ └── schema.rs
│ ├── views
│ │ └── index.html
│ └── Cargo.toml
├── frontend
│ ├── bundles
│ │ └── index.tsx
│ ├── public
│ │ ├── images
│ │ │ ├── favicon.ico
│ │ │ ├── logo192.png
│ │ │ └── logo512.png
│ │ ├── pwa.json
│ │ └── robots.txt
│ ├── src
│ │ ├── containers
│ │ │ ├── Home.tsx
│ │ │ └── Todo.tsx
│ │ ├── hooks
│ │ │ └── useQueryParam.ts
│ │ ├── images
│ │ │ ├── logo2.svg
│ │ │ ├── logo.svg
│ │ │ └── plus.svg
│ │ ├── types
│ │ │ ├── global.d.ts
│ │ │ ├── plugin-dev.d.ts
│ │ │ ├── rust.d.ts
│ │ │ └── untyped-modules.d.ts
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── dev.tsx
│ │ ├── reportWebVitals.js
│ │ └── setupDevelopment.tsx
│ ├── tests
│ │ └── example.spec.ts
│ ├── package.json
│ ├── package-lock.json
│ ├── playwright.config.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── migrations
│ ├── 00000000000000_diesel_initial_setup
│ │ ├── down.sql
│ │ └── up.sql
│ ├── 00000000000001_utc
│ │ ├── down.sql
│ │ └── up.sql
│ └── 00000000000002_todos
│ ├── down.sql
│ └── up.sql
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── diesel.toml
├── README.md
└── tree
.cargo
├── admin
│ └── dist
│ ├── admin.144d5956.css
│ ├── admin.144d5956.css.map
│ ├── admin.67d0b27a.js
│ ├── admin.67d0b27a.js.map
│ └── admin.html
├── bin
│ ├── backend
│ │ ├── src
│ │ │ └── backend.rs
│ │ └── Cargo.toml
│ ├── dsync
│ │ ├── src
│ │ │ ├── dsync.rs
│ │ │ └── lib.rs
│ │ └── Cargo.toml
│ ├── frontend
│ │ ├── src
│ │ │ └── frontend.rs
│ │ └── Cargo.toml
│ ├── fullstack
│ │ ├── src
│ │ │ └── fullstack.rs
│ │ └── Cargo.toml
│ └── tsync
│ ├── src
│ │ ├── lib.rs
│ │ └── tsync.rs
│ └── Cargo.toml
└── config
@AnthonyMichaelTDM thanks a bundle for the write-up and investigation