我用50亿字节的内存写入操作测试了go
和rust
的性能,发现go
的内存操作比rust
快!
我并不是一次性分配50亿字节内存,而是每次调用函数写入100000(大约100K不到一点)字节,反复调用5万次。我测试了两种写入方式,一种是初始化预分配内存的,另一种是初始化不分配内存的。结果是预分配模式下,go
大大领先rust
,速度达到rust
的1.78倍;不预先分配内存模式下,双方性能相当。
go
源代码(app.go
和app_test.go
)
//app.go
package main
import (
"fmt"
"time"
)
const L = 100000
const N = 50000
//预分配内存模式
func preAppend() []byte {
buf := make([]byte, 0, L)
for i := 0; i < L; i++ {
buf = append(buf, 's')
}
return buf
}
//不分配内存模式
func normalAppend() []byte {
buf := make([]byte, 0)
for i := 0; i < L; i++ {
buf = append(buf, 's')
}
return buf
}
func main() {
start := time.Now()
for i := 0; i < N; i++ {
preAppend()
}
fmt.Printf("preAppend Time:%v\n", time.Now().Sub(start).Seconds())
start = time.Now()
for i := 0; i < N; i++ {
normalAppend()
}
fmt.Printf("normalAppend Time:%v\n", time.Now().Sub(start).Seconds())
}
//app_test.go
package main
import (
"testing"
)
func TestNormalAppend(t *testing.T) {
for i := 0; i < N; i++ {
normalAppend()
}
println("TestNormalAppend")
}
func TestPreAppend(t *testing.T) {
for i := 0; i < N; i++ {
preAppend()
}
println("TestPreAppend")
}
rust
源代码:
//main.rs
use std::time;
const N: isize = 50000;
const L: usize = 100000;
fn main() {
let start = time::SystemTime::now();
for _i in 0..N {
pre_append();
}
let end = time::SystemTime::now();
println!("pre_append Time:{:?}", end.duration_since(start).unwrap());
let start = time::SystemTime::now();
for _i in 0..N {
normal_append();
}
let end = time::SystemTime::now();
println!(
"normal_append Time:{:?}",
end.duration_since(start).unwrap()
);
}
//不分配内存模式
fn normal_append() -> String {
let mut buf = String::new();
let mut i: usize = 0;
while i < L {
buf.push('s');
i += 1;
}
return buf;
}
//预分配内存模式
fn pre_append() -> String {
let mut buf = String::with_capacity(L);
let mut i: usize = 0;
while i < L {
buf.push('s');
i += 1;
}
return buf;
}
#[cfg(test)]
mod tests {
use crate::normal_append;
use crate::pre_append;
use std::fmt::Error;
const N: isize = 50000;
#[test]
fn test_pre_append() -> Result<(), Error> {
for _i in 0..N {
pre_append();
}
Ok(())
}
#[test]
fn test_normal_append() -> Result<(), Error> {
for _i in 0..N {
normal_append();
}
Ok(())
}
}
下面是各种模式下的运行结果:
测试模式的比拼
go
语言:
$ go test -v .
=== RUN TestNormalAppend
TestNormalAppend
--- PASS: TestNormalAppend (11.51s)
=== RUN TestPreAppend
TestPreAppend
--- PASS: TestPreAppend (6.63s)
PASS
ok app_test_bench 18.146s
运行 TestNormalAppend
的时间是11.51秒。
运行TestPreAppend
的时间是6.63秒。
rust
语言:
$ cargo t
Compiling string_test v0.1.0 (/home/fuhz/src/rust_test/string_test)
Finished test [unoptimized + debuginfo] target(s) in 0.37s
Running unittests src/main.rs (target/debug/deps/string_test-cf0723c9e88581f5)
running 2 tests
test tests::test_normal_append has been running for over 60 seconds
test tests::test_pre_append has been running for over 60 seconds
test tests::test_pre_append ... ok
test tests::test_normal_append ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 78.64s
test_normal_append
对应go
程序中的TestNormalAppend
,test_pre_append
对应go
程序中的TestPreAppend
,两者的运行时间都超过了60秒,无法显示精确时间。
Debug模式的比拼
$ go build
$ ./app_test_bench
preAppend Time:5.864950476
normalAppend Time:12.123574635
cargo build
Compiling string_test v0.1.0 (/home/fuhz/src/rust_test/string_test)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
$ target/debug/string_test
pre_append Time:69.011713959s
normal_append Time:69.313903563s
go
程序中,preAppend
用时5.865秒,normalAppend
用时12.123秒;rust
程序中,pre_append
用时69.012秒,normal_append
用时69.314秒。
Release模式的比拼
$ go build -ldflags '-s -w'
$ ./app_test_bench
preAppend Time:6.072494913
normalAppend Time:10.464082479
$ cargo build --release
Compiling string_test v0.1.0 (/home/fuhz/src/rust_test/string_test)
Finished release [optimized] target(s) in 0.30s
$ target/release/string_test
pre_append Time:10.794002015s
normal_append Time:10.808211814s
go
程序中,preAppend
用时6.072秒,normalAppend
用时10.464秒;rust
程序中,pre_append
用时10.794秒,normal_append
用时10.808秒。
有疑问加站长微信联系(非本文作者))

你这两段代码并不等价,rust保证了String内部为utf-8编码,而go没有这种保证,String::push需要先计算Char的长度,然后将其转换为u8或者u8切片才能进行添加。
你应该使用Vec<u8>做这个实验