为什么 Rust 链接器在增量构建时没有更快?

at5*_*321 5 linker linkage rust rust-cargo

我是 Rust 的新手,我试图理解为什么 Rust 不能构建得更快。我专门讨论最常见的情况,我对我的一个源文件做了一个小改动,然后我需要等待几秒钟cargo build才能完成它的工作。即使我的应用程序代码非常小,例如,如果我添加对 MySQL 和 Rocket 的依赖项,这两个 crate 也会附带它们自己的依赖项,显然这就是构建过程显着变慢的原因。显然,“慢”的意思是非常主观的,但是如果我需要等待 5-10 秒才能完成一天一百次的事情,我想知道为什么需要这样,我是否遗漏了什么.

在我看来,Cargo 并没有浪费太多时间来检查它是否需要重新编译这 200-300 个(子)依赖项中的任何一个,但需要这么长时间的是链接器。我试图了解链接器无法以某种方式优化该过程是否存在客观原因。例如,是否有可能以某种方式将整个 MySQL 和 Rocket 依赖项构建和缓存到两个更大的库中,并避免每次都执行所有这些工作?即使以二进制文件中的一些代码重复为代价。

顺便说一句,我尝试了 LLD 链接器(我使用的是 Ubuntu),它在一定程度上加快了速度,但它似乎仍然受到大量子依赖项的严重影响。

Jis*_*ikh 1

例如,是否有可能以某种方式构建并缓存整个 MySQL 和 Rocket 依赖项到两个更大的库中,并避免每次都执行所有这些工作?

我们可以将静态编译的 MySQL/Rocket 库链接到我们的 Rust 项目中(通过 C++ 包装器)。我们来试试吧。

  1. 创建build.rs文件,我们将使用 cxx crate 将 MySQL 和 Rocket 依赖项编译成静态库。
extern crate cxx_build;

fn main() {
    cxx_build::bridge("src/wrapper.rs")
        .file("src/mysql_wrapper.cpp")
        .file("src/rocket_wrapper.cpp")
        .flag("-std=c++17")
        .compile("mysql_wrapper", "rocket_wrapper");
}
Run Code Online (Sandbox Code Playgroud)
  1. 在我们的 src 目录中创建名为wrapper.rsmysql_wrapper.cpp和的包装文件rocket_wrapper.cpp
// mysql_wrapper.cpp

#include <mysql.h>

void init_mysql() {
    mysql_library_init(0, NULL, NULL);
}

void cleanup_mysql() {
    mysql_library_end();
}
Run Code Online (Sandbox Code Playgroud)
// rocket_wrapper.cpp

#include <rocket.h>

#include <iostream>

extern "C" {
    void init_rocket() {
        std::vector<std::string> args = { "myapp", "--port=8000" };
        rocket::config::Config config;
        config.from_args(args);
        rocket::ignite(config);
    }

    void stop_rocket() {
        rocket::shutdown();
    }
}
Run Code Online (Sandbox Code Playgroud)
// wrapper.rs

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        fn init_mysql();
        fn cleanup_mysql();
        fn init_rocket();
        fn stop_rocket();
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 修改 Rust 代码 ( main.rs) 以链接静态库,而不是直接链接 MySQL 和 Rocket 依赖项。
// main.rs

mod ffi {
    #[cxx::bridge]
    mod ffi {
        unsafe extern "C++" {
            fn init_mysql();
            fn cleanup_mysql();
            fn init_rocket();
            fn stop_rocket();
        }
    }
}

fn main() {
    unsafe {
        ffi::init_mysql();
    }
    // Usage of MySQL lib here
    unsafe {
        ffi::cleanup_mysql();
    }

    unsafe {
        ffi::init_rocket();
    }
    // Usage of Rocket f/work here
    unsafe {
        ffi::stop_rocket();
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 构建项目
cargo build
Run Code Online (Sandbox Code Playgroud)

就是这个。这将包括 mysql 和 Rocket 作为静态库,并且不会Cargo.toml作为直接依赖项进行链接。我不确定我们是否会获得真正显着的收益,它可能取决于项目结构,并且只有在您不打算更新这些依赖项的版本时才应该使用。我可能需要进行性能检查,但稍后。