猿问

golang循环初始比剩余慢

我是 golang 的新手,在做这个 poc 时,我注意到在运行 for 循环时有一个奇怪的行为。


    package main;


    import (

        "log"

        "strings"

        "time"

    )


    type data struct {

        elapseTime int64

        data string

    }




    func main(){

        for i := 0 ; i < 10; i++{


            c := make(chan data);

            go removeDuplicates("I love oranges LALALA I LOVE APPLES LALALA XD", c);

            log.Printf("%v", <- c);

        }

    }






    func removeDuplicates(value string , c chan data){

        start := time.Now();

        var d = data{};

        var r string;

        value = strings.ToLower(value);

        var m = make(map[string]string);

        splitVals := strings.Split(value, " ");

        for _, element := range splitVals {

            if _, ok := m[element]; ok == false {

                m[element] = element;

            }

        }

        for k, _ := range m {

            r = r + k + " ";

        }

        d.data = strings.TrimRight(r, "");

        d.elapseTime = time.Since(start).Nanoseconds();

        c <- d;

    }

实际上,我想要实现的是删除一个简单字符串的重复项,并将这些信息连同所花费的时间一起打印出来。一个循环通过 go routine 运行 10 次,等待通过通道的响应。


2019/05/24 00:55:49 {18060 i love oranges lalala apples xd }

2019/05/24 00:55:49 {28930 love oranges lalala apples xd i }

2019/05/24 00:55:49 {10393 i love oranges lalala apples xd }

2019/05/24 00:55:49 {1609 oranges lalala apples xd i love }

2019/05/24 00:55:49 {1877 i love oranges lalala apples xd }

2019/05/24 00:55:49 {1352 i love oranges lalala apples xd }

2019/05/24 00:55:49 {1708 i love oranges lalala apples xd }

2019/05/24 00:55:49 {1268 apples xd i love oranges lalala }

2019/05/24 00:55:49 {1736 oranges lalala apples xd i love }

2019/05/24 00:55:49 {1037 i love oranges lalala apples xd }

这是我所看到的:循环的前几次打印(无论是单循环还是 100x 循环都无关紧要)将比循环的其余部分慢得多。这是有原因的吗?(执行时间以纳秒为单位)


编辑:删除开关部分,因为人们对这个问题感到困惑。


蝴蝶刀刀
浏览 90回答 1
1回答

墨色风雨

并发不是并行。通道的这种特殊使用结果与仅从 返回值非常相似removeDuplicates,只是两个 goroutine 需要协调它们对通道的使用会产生额外的开销。具体来说:循环的每次迭代都有自己的通道,每个通道只能容纳一个元素。在所有语句都已执行之前,循环无法继续下一次迭代,包括对该语句的调用log.Printf阻塞,直到从通道接收到值。removeDuplicates检测到实时时间已经过去了多少,而不是在解决问题上花费了多少时间。这是评论说它首先不是一个很好的基准的众多原因之一。推测:在循环的前几次迭代中,goroutine 可能removeDuplicates会初始化start,然后将执行时间交给主 goroutine。然后 main goroutine 立即检查 mutex on c,发现它还不能做任何事情,并返回给调度程序,所有这些检查和上下文切换增加了数千纳秒(关心这通常是微基准测试的味道)到goroutineremoveDuplicates的实时执行。main在几次迭代之后,某些东西(也许是 Go 运行时)发现了在返回之前永远无法取得进展的事实removeDuplicates,并且避免了上下文切换。我知道此时您对解释比建议更感兴趣,但如果我不指出比较 Go 和 Java 的基准已经存在,我会觉得很不负责任。即使您想自己编写,我也建议使用类似的方法:根据需要完成的任务定义基准程序,然后使用每种语言(或框架)中可用的最佳工具来完成工作具有良好的性能。
随时随地看视频慕课网APP

相关分类

Go
我要回答