为什么 Go 的构建速度比 Rust 快?

huangapple go评论90阅读模式
英文:

why is the Go build faster than the Rust one?

问题

我正在练习在Golang和Rust中使用LeetCode编写解决方案。

我在Golang和Rust中为这个问题编写了解决方案。以下是Golang的代码:

package main

func letterCombinations(digits string) []string {
	if len(digits) == 0 {
		return []string{}
	}

	letters := [][]string{
		{},
		{},
		{"a", "b", "c"},
		{"d", "e", "f"},
		{"g", "h", "i"},
		{"j", "k", "l"},
		{"m", "n", "o"},
		{"p", "q", "r", "s"},
		{"t", "u", "v"},
		{"w", "x", "y", "z"},
	}

	var gen func(index int, digits string) []string
	gen = func(index int, digits string) []string {
		result := make([]string, 0)
		row := letters[int(digits[index]-'0')]
		index++
		for _, letter := range row {
			if index < len(digits) {
				for _, res := range gen(index, digits) {
					result = append(result, letter+res)
				}
			} else {
				result = append(result, letter)
			}
		}

		return result
	}

	return gen(0, digits)
}

func main() {
	for i := 0; i < 10000; i++ {
		letterCombinations("23456789")
	}
}

以下是Rust的代码:

struct Solution;

impl Solution {
    pub fn letter_combinations(digits: String) -> Vec<String> {
        if digits.len() == 0 {
            return vec![];
        }

        let letters: [[char; 5]; 10] = [
            [0 as char, '0', '0', '0', '0'],
            [0 as char, '0', '0', '0', '0'],
            [3 as char, 'a', 'b', 'c', '0'], // 2
            [3 as char, 'd', 'e', 'f', '0'], // 3
            [3 as char, 'g', 'h', 'i', '0'], // 4
            [3 as char, 'j', 'k', 'l', '0'], // 5
            [3 as char, 'm', 'n', 'o', '0'], // 6
            [4 as char, 'p', 'q', 'r', 's'], // 7
            [3 as char, 't', 'u', 'v', '0'], // 8
            [4 as char, 'w', 'x', 'y', 'z'], // 9
        ];

        fn gen(index: usize, digits: &str, letters: [[char; 5]; 10]) -> Vec<String> {
            let result_capacity = (4 as usize).pow(digits.len() as u32);
            let mut result: Vec<String> = Vec::with_capacity(result_capacity);
            let iletters = digits.chars().nth(index).unwrap().to_digit(10).unwrap() as usize;
            let row = letters[iletters];
            let index = index + 1;
            for i in 1..=row[0] as usize {
                if index < digits.len() {
                    let res = gen(index, digits, letters);
                    for j in 0..res.len() {
                        let mut line = String::with_capacity(res[j].len() + 1);
                        line.push(row[i]);
                        line.push_str(&res[j]);
                        result.push(line);
                    }
                } else {
                    result.push(row[i].to_string());
                }
            }
            return result;
        }

        return gen(0, &digits, letters);
    }
}

fn main() {
    for _i in 0..10000 {
        Solution::letter_combinations(String::from("23456789"));
    }
}

我在每个解决方案上运行了10,000次迭代。在我的笔记本电脑上,Golang解决方案需要60秒,而Rust解决方案需要556秒,大约慢了10倍。我猜这是因为Golang的垃圾收集器在程序运行期间不会将堆内存返回给操作系统,并且对每次迭代使用预分配的内存。而Rust的每次调用letterCombinations()函数都会从操作系统中分配内存并释放。所以Rust更慢。我的理解正确吗?

英文:

I am practicing at leedcode writing solution in golang and rust.
I wrote a solution for this problem https://leetcode.com/problems/letter-combinations-of-a-phone-number/ in golang and in rust

package main
func letterCombinations(digits string) []string {
if len(digits) == 0 {
return []string{}
}
letters := [][]string{
{},
{},
{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;},
{&quot;d&quot;, &quot;e&quot;, &quot;f&quot;},
{&quot;g&quot;, &quot;h&quot;, &quot;i&quot;},
{&quot;j&quot;, &quot;k&quot;, &quot;l&quot;},
{&quot;m&quot;, &quot;n&quot;, &quot;o&quot;},
{&quot;p&quot;, &quot;q&quot;, &quot;r&quot;, &quot;s&quot;},
{&quot;t&quot;, &quot;u&quot;, &quot;v&quot;},
{&quot;w&quot;, &quot;x&quot;, &quot;y&quot;, &quot;z&quot;},
}
var gen func(index int, digits string) []string
gen = func(index int, digits string) []string {
result := make([]string, 0)
row := letters[int(digits[index]-&#39;0&#39;)]
index++
for _, letter := range row {
if index &lt; len(digits) {
for _, res := range gen(index, digits) {
result = append(result, letter+res)
}
} else {
result = append(result, letter)
}
}
return result
}
return gen(0, digits)
}
func main() {
for i := 0; i &lt; 10000; i++ {
letterCombinations(&quot;23456789&quot;)
}
}

and this in rust

struct Solution;
impl Solution {
pub fn letter_combinations(digits: String) -&gt; Vec&lt;String&gt; {
if digits.len() == 0 {
return vec![];
}
let letters: [[char; 5]; 10] = [
[0 as char, &#39;0&#39;, &#39;0&#39;, &#39;0&#39;, &#39;0&#39;],
[0 as char, &#39;0&#39;, &#39;0&#39;, &#39;0&#39;, &#39;0&#39;],
[3 as char, &#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;0&#39;], // 2
[3 as char, &#39;d&#39;, &#39;e&#39;, &#39;f&#39;, &#39;0&#39;], // 3
[3 as char, &#39;g&#39;, &#39;h&#39;, &#39;i&#39;, &#39;0&#39;], // 4
[3 as char, &#39;j&#39;, &#39;k&#39;, &#39;l&#39;, &#39;0&#39;], // 5
[3 as char, &#39;m&#39;, &#39;n&#39;, &#39;o&#39;, &#39;0&#39;], // 6
[4 as char, &#39;p&#39;, &#39;q&#39;, &#39;r&#39;, &#39;s&#39;], // 7
[3 as char, &#39;t&#39;, &#39;u&#39;, &#39;v&#39;, &#39;0&#39;], // 8
[4 as char, &#39;w&#39;, &#39;x&#39;, &#39;y&#39;, &#39;z&#39;], // 9
];
fn gen(index: usize, digits: &amp;str, letters: [[char; 5]; 10]) -&gt; Vec&lt;String&gt; {
let result_capacity = (4 as usize).pow(digits.len() as u32);
let mut result: Vec&lt;String&gt; = Vec::with_capacity(result_capacity);
let iletters = digits.chars().nth(index).unwrap().to_digit(10).unwrap() as usize;
let row = letters[iletters];
let index = index + 1;
for i in 1..= row[0] as usize {
if index &lt; digits.len() {
let res = gen(index, digits, letters);
for j in 0..res.len() {
let mut line = String::with_capacity(res[j].len() + 1);
line.push(row[i]);
line.push_str(&amp;res[j]);
result.push(line);
}
} else {
result.push(row[i].to_string());
}
}
return result;
}
return gen(0, &amp;digits, letters);
}
}
fn main() {
for _i in 0..10000 {
Solution::letter_combinations(String::from(&quot;23456789&quot;));
}
}

Where I run each solution through 10 000 iterations.
Golang solution takes 60 seconds on my laptop.
Rust solution takes 556 seconds which is about 10 times slower.
I guess it because golang garbage collector does not return heap memory to OS during program and use pre-allocated memory for each iteration.
But rust every call of function letterCombinations() allocates memory from OS and frees it back. So rust slower.
Am I correct?

答案1

得分: 2

如其他地方所指出(例如,https://stackoverflow.com/q/25255736/1256452),如果你在调试模式下编译Rust代码,它往往会很慢。如果你开启优化,它会变得快得多。

值得在这里提到的是,如果你允许(或告诉)Rust编译器,它们会进行大量的优化。当以发布模式运行时,cargo build系统会进行这种优化。这种优化可能会使调试变得非常困难!所以如果你想让代码正常工作,你可能还不想这样做。

Go编译器传统上只进行相对简单的优化。1因此,go build目前没有优化级别或标志,除了-N用于禁用优化和-l用于禁用内联


1这种优化并不是无用的,但它不是rustc在高优化级别下所做的那种超级复杂的优化,该优化会使得在调试器中跟踪代码变得不可能。

英文:

As noted elsewhere (e.g., https://stackoverflow.com/q/25255736/1256452), if you compile Rust code in debug mode, it tends to be slow. If you turn on optimization, it tends to be much faster.

It's worth mentioning here that Rust compilers will do a great deal of optimization if you let them (or tell them). The cargo build system will do this when run in release mode. This optimization can make debugging very hard! So if you're trying to get code working, you probably don't want to do this yet.

Go compilers traditionally just do relatively simple optimization.<sup>1</sup> So go build currently doesn't have an optimization level or flag, except for -N to disable optimization and -l to disable inlining.


<sup>1</sup>This optimization is not useless, but it's not the kind of super-fancy makes-following-your-code-in-the-debugger-impossible optimization that rustc does at high optimization levels.

huangapple
  • 本文由 发表于 2021年9月5日 19:28:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/69062965.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定