< 返回版块

使用may的正确姿势

Xudong Huang 发表于

标签:may,coroutine 浏览:534 评论:6

最近看了voner同学的bench,里面特意提到了may,他还发了一个比较的博文

基本上这就是一个echo server的bench。其实也没有什么好说的,因为may的主页上已经提供了一份标准的实现,效果大家可以自己跑。但是在他的bench里may突然变慢了,这里主要分析下,为什么在这个实现里,may会变慢。

首先看看线程模型。同一个connection里的listener,clone出来好几份,然后分给不同的thread来使用。这样做虽然并不太影响效率,但是may里不是这样用的。May只需要一个listener就够了,没有必要clone几份。更不需创建线程来分担工作。推荐创建协程来完成所有的工作,写过go的同学应该很熟悉了。

这里面有个问题,在线程的环境里调用may的API会有什么效果?因为May的API都是线程兼容的,所有并没有影响任何语义。但是,may的API在线程里面调用,会退化成Block模式的,也就是说这几个线程listener都是block调用的,这个它慢下来的一个原因。

另外voner同学没有使用go!macro,而是直接使用了unsafe的spawn。把整个body都放在了unsafe的块里,这样做就失去了编译器优化的机会,编译器是不会优化unsafe块的。所以你的body就是一个debug版本。这是它慢下来的另外一个原因。

另外在他的注释里有这么一段话

The may library uses N:M threading with work stealing of coroutine threads. This completely disregards all the compile-time thread safety guarantees of Rust and turns Rust into a C++ with better package management.

其实这段话很有迷惑性,但是我们可以换一个问题问问,下面这个数据结构是不是线程安全的

struct coroutine {
    stack: Mutext<Vec<u8>>
}

如果你觉得他是线程安全的,那May就是安全的。May在每次调度切换线程的时候都已经有类似的保护机制了,你不可能同时在两个地方运行协程。stack数据就像协程的一个struct变量一样。

This is OK in our case ‒ we do basically nothing here and we have no non-Send data on the thread. But it's hard to ensure in the general case (you'd have to check the stacks of all the dependencies and you'd have to make sure none of your dependencies uses TLS). Still, the check lies with the user of the library.

这也是个老问题了,TLS访问在单线程模型下也有问题,而且是个通用的问题,future based也同样存在不确定的问题。只让May挨板子有点说不过去,就好像其他的方案都是安全的一样。有兴趣的同学可以去google搜索一下相关的ThreadLocal和future的问题,比如这篇:https://particular.net/blog/the-dangers-of-threadlocal

那份代码小修改下变成这样

fn run_may(listener: TcpListener) {
    may::config().set_io_workers(num_cpus::get());
    // May doesn't seem to support direct conversion
    // should be: let listener = may::io::CoIo::new(listener);
    let raw_fd = listener.into_raw_fd();
    let listener = unsafe { MayTcpListener::from_raw_fd(raw_fd) };
    go!(move || {
        while let Ok((mut connection, _address)) = listener.accept() {
        go!(move || {
                let mut buf = [0u8; BUF_SIZE];
                for _ in 0..*EXCHANGES {
                    connection.read_exact(&mut buf[..]).unwrap();
                    connection.write_all(&buf[..]).unwrap();
                }
            });
        }
    });
}

咱英语不行,说也说不过人家,自己默默的做事情就好了。桃李无言,下自成蹊。

评论区

ChaosBot 2018-02-04T07:32:25.283742

学习了! 那个退化为block是指在多线程模式下?

作者 Xudong Huang 2018-02-04T07:46:16.619549

跟多线程没有关系,只要是在线程环境下,就是block模式的,在协程环境中就是nonblock的,May的实现会在内部检测到

ChaosBot 2018-02-04T08:49:42.913797

了解

@Xudong Huang 跟多线程没有关系,只要是在线程环境下,就是block模式的,在协程环境中就是nonblock的,May的实现会在内部检测到

Mike Tang 2018-02-05T03:29:32.819954

学习了,最近在帮公司研究区块链的东西,所以暂时还没有投入到may工程里面来。

腹黑猫 2018-02-26T03:22:34.728003

学习了

sunshineboy 2018-03-16T01:56:02.817921

膜拜

1 共 6 评论, 共 1 页