当前位置:首页 > 综合热点 > 正文

【翻译】为什么Rust可执行文件如此巨大?

摘要: 【翻译】为什么Rust可执行文件如此巨大?最佳答案53678位专家为你答疑解惑【翻译】为什么Rust可执行文件如此巨大?问题:W...

【翻译】为什么Rust可执行文件如此巨大?

最佳答案 53678位专家为你答疑解惑

【翻译】为什么Rust可执行文件如此巨大?

问题:

Why are Rust executables so huge?

为什么Rust可执行文件如此巨大?

Just having found Rust and having read the first two chapters of the documentation, I find the approach and the way they defined the language particularly interesting. So I decided to get my fingers wet and started out with Hello world...

我刚刚发现Rust,并且阅读了文档的前两个章节,发现他们定义语言的方法和方式特别有趣的。所以我决定动动我的手指,开始敲Hello world...

I did so on Windows 7 x64, btw.

顺便说一句,我是在Windows 7 x64系统上这么做的。

fn main() {

? ? println!("Hello, world!");

}

Issuing?cargo build?and looking at the result in?targets\debug?I found the resulting?.exe?being 3MB. After some searching (documentation of cargo command line flags is hard to find...) I found?--release?option and created the release build. To my surprise, the .exe size has only become smaller by an insignificant amount: 2.99MB instead of 3MB.

问题是在cargo build,去看targets\debug下的结果,我发现结果.exe文件有3MB.查找了一些之后(很难找到cargo命令行标记的文档...),我发现 --release 选项,并且创建release构建。令我惊讶的是,.exe文件大小仅仅变小了点儿,从3MB替换为2.99MB。

So, confessing I am a newbie to Rust and its ecosystem, my expectation would have been that a Systems Programming language would produce something compact.

所以,我承认对于Rust和它的生态是一个新手,我的期望是作为一个系统编程语言,它应该生成一些紧凑的东西。

Can anyone elaborate on what Rust is compiling to, how it can be possible it produces such huge images from a 3 liner program? Is it compiling to a virtual machine? Is there a strip command I missed (debug info inside the release build?)? Anything else which might allow to understand what is going on?

有人能详细说明一下Rust编译的是什么,它怎么可能从一段3行程序中产生如此巨大的镜像?它是编译到一个虚拟机么?是有一个我疏忽的瘦身命令(在release构建中有调试信息?)么?还是有其它的能让我明白发生了什么的原因?

评论:

I think 3Mb contains not only Hello World, but also all the needed environment for the platform. The same thing can be seen with Qt. That does not mean if you write a 6-line program the size will become 6 Mb. It will stay at 3Mb and will grow very slowly after that.

我认为3Mb不仅包含了Hello World,而且也包含了平台全部所需要的环境。Qt也有同样的处理。那不意味着如果你写一个6行的程序,文件大小就会变成6Mb.它将保持在3Mb,并且之后增长将非常缓慢。

@AndreiNikolaenko I am aware of that. But this hints that either they do not handle libraries as C does, adding only what is required to an image or that something else is going on.?

@AndreiNikolaenko 我明白。但是这表明,要么他们不能像C那样处理库,只能向镜像添加所需要的,要么他们正在做一些其他事情。

@user2225104 See my answer, RUST handles libraries in the same (or similar) way as C does, but by default C does not compile static libraries into your program (at least, on C++).

@user2225104 看我的答案,RUST处理库和C是相同(或相似)的,但是默认C是不编译静态库进入程序的(至少在C++中)。

Very related:?Do DLLs built with Rust require libgcc.dll on run time?.

非常相关:Rust构建的DLL在运行时需要libgcc.dll么?

Is this outdated now? With rustc version 1.35.0 and no cli options I get an exe that is 137kb in size. Does it automatically compile dynamically linked now or did something else happen in the meantime?

这个现在是过时了么?在rustc 1.35.0版本下,没有命令行参数,我得到一个exe文件是137kb。现在它是自动编译动态链接还是与此同时发生了其他事情?

回答1(selected):

Rust uses static linking to compile its programs, meaning that all libraries required by even the simplest?Hello world!?program will be compiled into your executable. This also includes the Rust runtime.

Rust使用静态链接来编译程序,意思是即使是最简单的Hello world!程序也需要将所有库编译进你的可执行文件。这也包含了Rust运行时情况。

To force Rust to dynamically link programs, use the command-line arguments?-C prefer-dynamic; this will result in a much smaller file size?but?will also require the Rust libraries (including its runtime) to be available to your program at runtime. This essentially means you will need to provide them if the computer does not have them, taking up?more?space than your original statically linked program takes up.

为了强制Rust动态链接程序,使用命令行参数 -C prefer-dynamic;这将导致文件大小变小了很多,但是这也要求在运行时你的程序Rust库(包括库的运行时)是可用的。这本质上意味着如果电脑中没有它们,你将需要提供它们,它们比你原来的静态链接程序占用更多空间。

For portability I'd recommend you statically link the Rust libraries and runtime in the way you have been doing if you were to ever distribute your programs to others.

从可移植性考虑,我建议你静态链接Rust库和运行时,依照此方法,如果你曾经分发你的程序给其他人,你是可以工作的。

评论:

@user2225104 Unsure about Cargo, but according to?this bug report on GitHub, this isn't yet possible unfortunately.

@user2225104 关于Cargo还不确定,但是不幸的是,根据在GitHub上的这个bug记录,这还不可能。

I don't think static linking explains the huge HELLO-WORLD. Shouldn't it only link in the parts of the libraries that are actually used, and HELLO-WORLD uses virtually nothing?

我认为静态链接不能解释HELLO-WORLD巨大的原因。它不是应该仅仅链接实际用到一部分库,而HELLO-WORLD几乎什么都没有用?

BitTickler?cargo rustc [--debug or --release] -- -C prefer-dynamic

备忘:cargo rustc [--debug or --release] -- -C prefer-dynamic

@daboross Thank you very much. I have been tracking?this related RFC. It's really a pity since Rust also targets system programming.

@daboross 非常感谢。我已经在跟踪相关的RFC。很遗憾,因为Rust还把系统编程作为目标。

@Nulik: Yes, by default, but that's because Rust defaults to static builds (all dependencies, including runtime, included), while Go links its runtime dynamically. On my CentOS 7 system, Go's?helloworld?compiles to ~76K, but on top of standard stuff, it takes a runtime dynamic dependency on?libgo.so, which is over 47M. The default Rust?helloworld?(as made with?cargo new) doesn't have any unique dynamic dependencies, holding everything but basic C runtime stuff in a 1.6M executable; with tweaks (optimize for size, using LTO, aborting on panic), it drops to 0.6M.

@Nulik: 是的,默认情况下是这样,但是那是因为Rust默认是静态编译(所有依赖,包含运行时,已经包含的),而Go是动态的链接运行时的。在我的CentOS 7 系统上,Go的helloworld? 编译为76K,但是在标准的事务上面,运行时需要动态的依赖libgo.so,它超过了47M。Rust的helloworld(像用cargo new生成的)默认没有任何独特的动态依赖,除了基于C的运行时内容,其他所有内容都包含在一个1.6M的可执行文件中;做一些调整(优化大小,使用LTO, 终止painc),它下降到0.6M。

回答2:

I don't have any Windows systems to try on, but on Linux, a statically compiled Rust hello world is actually smaller than the equivalent C. If you are seeing a huge difference in size, it is probably because you are linking the Rust executable statically and the C one dynamically.

我没有任何windows系统去尝试,但是在Linux,静态编译Rust hello wolrd实际上比等效的C更小。如果你看到大小有巨大的差异,那可能是因为你静态的链接了Rust可执行文件,而C是动态的。

With dynamic linking, you need to take the size of all the dynamic libraries into account too, not just the executable.

对于动态链接,你也需要将所有的动态库的大小统计进去,而不仅仅是可执行文件。

So, if you want to compare apples to apples, you need to make sure either both are dynamic or both are static. Different compilers will have different defaults, so you can't just rely on the compiler defaults to produce the same result.

所以,如果你想一一进行比较,你需要确保要么两者都是动态的,要么两者都是静态的。不同的编译器会有不同的默认值,所以你不能仅仅依靠编译器默认值来产生相同的结果。

If you're interested, here are my results:

如果你有兴趣,这里是我的结果:

-rw-r--r-- 1 aij aij? ? 63 Apr? 5 14:26 printf.c

-rwxr-xr-x 1 aij aij? 6696 Apr? 5 14:27 printf.dyn

-rwxr-xr-x 1 aij aij 829344 Apr? 5 14:27 printf.static

-rw-r--r-- 1 aij aij? ? 59 Apr? 5 14:26 puts.c

-rwxr-xr-x 1 aij aij? 6696 Apr? 5 14:27 puts.dyn

-rwxr-xr-x 1 aij aij 829344 Apr? 5 14:27 puts.static

-rwxr-xr-x 1 aij aij? 8712 Apr? 5 14:28 rust.dyn

-rw-r--r-- 1 aij aij? ? 46 Apr? 5 14:09 rust.rs

-rwxr-xr-x 1 aij aij 661496 Apr? 5 14:28 rust.static

These were compiled with gcc (Debian 4.9.2-10) 4.9.2 and rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (built 2015-04-03), both with default options and with?-static?for gcc and?-C prefer-dynamic?for rustc.

这些使用gcc (Debian 4.9.2-10) 4.9.2 和? rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (built 2015-04-03) 编译,两者都是默认选项,并且对于 gcc 使用 -static ,对于rustc 使用-C prefer-dynamic。

I had two versions of the C hello world because I thought using?puts()?might link in fewer compilation units.

我有两个版本的C hello world,因为我本来想使用 puts() 可能链接编译单元会少一些。

If you want to try reproducing it on Windows, here are the sources I used:

如果你想在windows尝试重新生成,这里有我使用的源代码:

printf.c:

#include <stdio.h>

int main() {

? printf("Hello, world!\n");

}

puts.c:

#include <stdio.h>

int main() {

? puts("Hello, world!");

}

rust.rs

fn main() {

? ? println!("Hello, world!");

}

Also, keep in mind that different amounts of debugging information, or different optimization levels would also make a difference. But I expect if you are seeing a huge difference it is due to static vs. dynamic linking.

还有,请记住,不同的大量的调试信息,或者不同的优化等级也会产生不同。但是我认为如果看到巨大的差异,那是由于静态和动态链接原因。

评论:

gcc is smart enough to do exactly the printf -> puts substitution itself, that's why results are identical.

gcc 足够智能,准确的将printf替换为puts,这就是为什么结果是相同的。

As of 2018 if you want a fair comparison do remember to "strip" the executables, as a hello world Rust executable on my system is a whopping 5.3MB but drops down to less than 10% of that when you remove all the debug symbols and such.

截止2018年,如果你想有一个公平的比较,记得给可执行文件瘦身,因为一个hello world Rust可执行文件,在我的系统中有5.3MB那么大,但是当你移除所有调试符号等,会下降到10%以下。

@MattiVirkkunen: Still the case in 2020; the natural size seems smaller (nowhere near 5.3M), but the ratio of symbols to code is still pretty extreme. The debug build, purely default options on Rust 1.34.0 on CentOS 7, stripped with?strip -s, drops from 1.6M to 190K. The release build (defaults plus?opt-level='s',?lto=true, and?panic='abort'?to minimize size) drops from 623K to 158K.

@MattiVirkkunen:? 到2020年,情况依然如此;自然大小似乎小一些(接近5.3M),但是符号与代码的比例仍然相当极端。在CentOS 7 上进行调式编译,Rust 1.34.0版本, 完全默认选项,瘦身采用?strip -s,结果从1.6M下降到190K。发布编译(默认值 加上 opt-level='s',?lto=true,? 和 panic='abort' 参数,使大小最小),从623K下降到158K。

Rust 让人奔溃的那些特性

作者 | William Woodruff

译者 | Arvin,责编 | 屠敏

头图 | CSDN 下载自东方 IC

出品 | CSDN(ID:CSDNnews)

以下为译文:

五年前,我写了一篇文章论述了我当时(至今)最喜欢的脚本语言Ruby让我讨厌的地方(https://blog.yossarian.net/2015/09/28/Five-Things-I-Hate-About-Ruby)。

今天,我将对我目前最喜欢的编译型语言:Rust做同样的事情。

就像最初写的关于Ruby的帖子一样,这些抱怨都是个人观点,反映了我目前对这个语言的最佳理解。就像写关于Ruby的文章一样,这篇也是出于对Rust的热爱而编写的。

闲话少说,让我们开始主题。

字符串地狱

我在开发过程中抱怨Rust中的字符串有两个大方向:

1.字符串类型之间的区别令人困惑

2.字符串类型之间进行转换的方法太多

字符串类型太多

我可以想到5种不同的方式来表示字符串[1],字符串视图或接受字符串形式的方法签名:

&str 用于表示借来的字符串

String用于表示自有的字符串

&OsStr 用于表示从操作系统借用的字符串

OsString 用于表示操作系统中自有的字符串

AsRef用于方法签名,表示一个廉价的&str引用

(我知道最后一个并不是真正的字符串类型,但是它经常出现在惯用的字符串处理代码中。)

作为Rust新手,这几种类型之间的区别非常令人困惑,也使得理解引用变得更加困难(为什么引用&String与&str不同?为什么我不能直接创建str?我从哪里得到&&str?)。

在字符串之间进行转换的方法太多

多种字符串类型和相关特征带来多种转换功能:

&str到String:在不考虑格式化转换或往返一个Vec或[u8]的情况下,至少存在这么多种,String::from,to_string,to_owned,into

String到&str:as_str,as_ref,Deref<Target=str>,&x[..]

适用于OsStr和CStr的类似(可能有损)方法

这些转换中的大多数在性能上是等效的,Rust社区似乎对哪些是“正确的”存在分歧。

我最终习惯于根据上下文使用不同的字符串(例如into(),表示要将a &str转换为a,String以便可以将其返回,to_owned()表示稍后将拥有该字符串的所有权)。

标准库差距

Rust标准库存在一些空白,这些空白使用户空间编程的各个方面都很痛苦:

当前没有获取用户主目录的方法。std::env::home_dir被明确标记为已弃用,并且该文档鼓励用户使用第三方库dirs(但是该库已经不再维护,详见GitHub链接:https://blog.yossarian.net/2020/05/20/Things-I-hate-about-rust%20/l%20fn:2)[2]。

没有标准的扩展方式~。std::fs::canonicalize支持.和 ..,但不支持~。这其实和上面一点有所重复。

无法通过系统shell调用命令。我知道system[3]命令有各种问题。我也同意它不应该是执行其他进程的默认接口,甚至应该被隔离以防止意外使用。但所有这些都没有改变这样一个事实--即这个命令偶尔会有用,在标准库中实现比最终开发人员直接使用sh-c更可靠。

没有标准的方法进行glob操作。似乎 glob第三方库是执行此操作的半官方方法。

这些都是公认的微小差距,所有这些都可以通过高质量的第三方库解决。但是它们会在开发过程中增加摩擦,鉴于Rust无摩擦的特性,摩擦是特别值得注意的。

特质(Traits)

我喜欢基于特质的组合。但我不喜欢下面这些东西:

被告知我忘记了使用use std::io::Read或者use std::io::Write的方法,但其实原因是我正在调用的方法已经被作用域中的一些东西调用了。我知道为什么 Rust会这样做,不然就会出现编译器警告,但是我仍然感觉很奇怪,尤其是在未使用导入的情况下。

为特质实现特质的语法。impl<T> for Trait for T where T: OtherTrait 虽然不算太糟糕,但它读起来不像impl Trait for OtherTrait那么自然。

有时Rust编译器rustc需要我在静态函数(non-self)特质函数中添加where Self: Sized。我仍然不明白为什么有时需要这样做,而有时则不需要。但我相信这是有正当理由的。

无需扩展的安全索引

给定一个固定数组x=[T; N]和类型为U(U的约束为U::MAX < N)的索引变量i,通过x[i] 索引始终是安全的。尽管如此,Rust编译器rustc希望程序员能够明确将i扩展到usize:

fn main {let lookup_table: [u8; 256]=[0_u8; 256];let index=5_u8;println!("{}", lookup_table[index]);}

失败结果:

error[E0277]: the type `[u8]` cannot be indexed by `u8`--> src/main.rs:4:20|4 | println!("{}", lookup_table[index]);| ^^^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`|=help: the trait `std::slice::SliceIndex<[u8]>` is not implemented for `u8`=note: required because of the requirements on the impl of `std::ops::Index<u8>` for `[u8]`

虽然这可以理解,但要求程序员要么在索引计划中的任何位置使用as usize(冗长,并掩盖了索引背后为 u8的意图),要么将索引本身变成usize(也掩盖了意图,并使算术运算变得更容易超出界限),还是有些强求。

最后一点:cargo install 有时不能成功

我不知道这是不是一个真正的错误,但是由于我被它坑了几次,所以我将它加了进去。

cargo install显然不知道如何发现带后缀的软件包版本。例如,如果我发布myfakepackage为version 0.0.1-alpha.0,cargo install则会报告如下错误:

$ cargo install myfakepackageerror: could not find `myfakepackage` in registry `https://github.com/rust-lang/crates.io-index`

你必须明确传递—version参数:

$ cargo install myfakepackage --version 0.0.1-alpha.0

总结

我还有其他一些想讨论的内容(对于不支持特质的核心类型的别名,程序包生态系统的样式有点像JS / -y),但是我认为这样做有可能对我非常满意的语言太过消极。

五年过去了,我仍然喜欢Ruby,并且对Rust感到乐观。

备注说明:

1.没包括CString和&CStr,因为它们主要用于FFI上下文,并且可以理解为是不同的。?

2.我知道在POSIX平台上可靠地获取用户的主目录实际上非常困难。但这并没有改变标准库应该尝试的事实。?

3.恰当的例子:CLI经常公开钩点和回调,这些钩点和回调能够使用shell语法编写很有用

作者:William Woodruff,研究和工程实践者,开源软件参与者,软件库Homebrew软件开发成员,kbsecret的首席维护者

原文:https://blog.yossarian.net/2020/05/20/Things-I-hate-about-rust

本文为 CSDN 翻译,转载请注明来源出处。

6月2日20:00,CSDN 创始人&董事长、极客帮创投创始合伙人蒋涛携手全球顶级开源基金会主席、董事,聚焦中国开源现状,直面开发者在开源技术、商业上的难题,你绝不可错过的开源巅峰对谈!立即免费围观:

?重磅!阿里巴巴开源首个边缘计算云原生项目 OpenYurt

?腾讯面试题: 百度搜索为什么那么快? | 原力计划

?都无代码了,还要程序员吗?

?附代码 | OpenCV实现银行卡号识别,字符识别算法你知多少?

?因为一个跨域请求,我差点丢了饭碗

?区块链的 Layer 2 扩容(Scaling)是否兑现了其承诺?

发表评论