传说rust性能高于go,我测试下来怎么go的内存操作比rust快!

fuhuizn · 2023-06-08 20:16:58 · 2940 次点击 · 预计阅读时间 5 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2023-06-08 20:16:58 的文章,其中的信息可能已经有所发展或是发生改变。

我用50亿字节的内存写入操作测试了gorust的性能,发现go的内存操作比rust快!

我并不是一次性分配50亿字节内存,而是每次调用函数写入100000(大约100K不到一点)字节,反复调用5万次。我测试了两种写入方式,一种是初始化预分配内存的,另一种是初始化不分配内存的。结果是预分配模式下,go大大领先rust,速度达到rust的1.78倍;不预先分配内存模式下,双方性能相当。

go源代码(app.goapp_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程序中的TestNormalAppendtest_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秒。


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2940 次点击  
加入收藏 微博
1 回复  |  直到 2023-07-18 15:00:03
yuanyan3060
yuanyan3060 · #1 · 2年之前

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

你应该使用Vec<u8>做这个实验

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传