使用异步时没有注意到任何性能改进

Not noticing any performance improvement when using async

我有一个执行 aws s3 cli 命令但参数不同的小程序。我正在使用 Command 箱子,命令进行网络调用并 returns 做出一些响应。起初我有这个同步和单线程实现:

fn make_call<'a>(_name: &'a str, _bucket_poll: &mut BucketPoll<'a>) -> Option<BucketDetails<'a>> {
    let invoke_result = invoke_network_call(_name);
    let mut bucket = BucketDetails::new(_name);
    match invoke_result {
        Ok(invoke_str) => {
            bucket.output = invoke_str;
            _bucket_poll.insert_bucket(bucket.clone());
            _bucket_poll.successful_count += 1;
            Some(bucket)
        }
        Err(_) => {
            _bucket_poll.insert_bucket(bucket);
            None
        }
    }
}
// I invoke this function in sequential order, something like
make_call('name_1');
make_call('name_2');
make_call('name_3');

因为我不太关心这个函数的执行顺序,所以我决定学习 Tokio 来帮助提高性能。我将 make_call 函数更改为异步:

async fn make_call_race() -> ExecutionResult {
    let bucket_poll = BucketPoll::new();
    let bucket_poll_guard = Arc::new(Mutex::new(bucket_poll));
    loop {
        let bucket_details = tokio::select! {
            Some(bucket_details) = make_call_async("name_1", &bucket_poll_guard) => bucket_details,
            Some(bucket_details) = make_call_async("name_2", &bucket_poll_guard) => bucket_details,
            Some(bucket_details) = make_call_async("name_3", &bucket_poll_guard) => bucket_details,
            Some(bucket_details) = make_call_async("name_4", &bucket_poll_guard) => bucket_details,
            else => { break }
        };
        success_printer(bucket_details);
    }
    // more printing, no more network calls
    ExecutionResult::Success
}

make_call_async 本质上与 make_call:

相同
async fn make_call_async<'a>(
    _name: &'a str,
    _bucket_poll_guard: &'a Arc<Mutex<BucketPoll<'a>>>,
) -> Option<BucketDetails<'a>> {
    {
        if let Ok(bucket_poll_guard) = _bucket_poll_guard.lock() {
            if bucket_poll_guard.has_polled(_name) {
                return None;
            }
        }
    }
    let invoke_result = invoke_network_call(_name);
    let mut bucket = BucketDetails::new(_name);
    match invoke_result {
        Ok(invoke_str) => {
            bucket.output = invoke_str;
            {
                if let Ok(mut bucket_poll_guard) = _bucket_poll_guard.lock() {
                    bucket_poll_guard.insert_bucket(bucket.clone());
                    bucket_poll_guard.successful_count += 1;
                }
            }
            Some(bucket)
        }
        Err(_) => {
            {
                if let Ok(mut bucket_poll_guard) = _bucket_poll_guard.lock() {
                    bucket_poll_guard.insert_bucket(bucket);
                }
            }
            None
        }
    }
}

当我 运行 异步版本时,我确实看到我的网络调用是随机顺序的,但我没有注意到任何加速。我将网络调用次数增加到约 50 次,但 运行 时间几乎相同,甚至更差。由于我是异步编程和 Rust 的新手,我想了解为什么我的异步实现似乎没有提供任何改进。

额外:

这里是 invoke_network_call 方法:

fn invoke_network_call(_name: &str) -> core::result::Result<String, AwsCliError> {
    let output = Command::new("aws")
        .arg("s3")
        .arg("ls")
        .arg(_name)
        .output()
        .expect("Could not list s3 objects");
    if !output.status.success() {
        err_printer(format!("Failed to list s3 objects for bucket {}.", _name));
        return Err(AwsCliError);
    }
    let output_str = get_stdout_string_from_output(&output);
    Ok(output_str)
}

编辑:yorodm 的评论很有道理。我所做的是使用 Tokio 的命令而不是 std::process 的命令,并使 invoke_network_call 异步。这使我的 运行 时间减少了一半。谢谢!

您可以使用 async version of Command.

重写 invoke_network_call
async fn invoke_network_call(_name: &str) -> core::result::Result<String, AwsCliError> {
    let output = tokio::process::Command::new("aws")
        .arg("s3")
        .arg("ls")
        .arg(_name)
        .output()
        .await
        .expect("Could not list s3 objects");
    if !output.status.success() {
        err_printer(format!("Failed to list s3 objects for bucket {}.", _name));
        return Err(AwsCliError);
    }
    let output_str = get_stdout_string_from_output(&output);
    Ok(output_str)
}

从而移除阻塞的 std::process::Command 调用。但是我会说,如果您要访问 AWS 服务 you should go with rusoto