27

talent-plan tidb 部分个人题解-week 2

 4 years ago
source link: http://xargin.com/talent-plan-week2-solution/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Week 2 的问题是 map reduce 优化,说实话,这周的示例代码写的不怎么样,不知道为什么都是同一个人的代码,同一个参数的名字还换来换去,读起来会浪费一些时间。一些缩写也让人比较困惑,比如 fs,bs,别人要稍微思考一下才能看懂这是什么东西,用 fileList 或者 bufferList 显然更好啊,可能样例代码的人是写 acm 出身,比较喜欢赶时间。虽然 Go 的 runtime 代码里也经常这么干,汗。

吐槽就到这里。题目是想让你先把 example 补全,能运行,然后再去实现自己的 map reduce 方法。要补两个地方,第一个是 run 函数里的 reduce phase,另一个是 worker 函数的 reduce phase 的具体实现工作。这里提示比较少,不过看着函数名和代码上下文花半个小时可能就猜出来怎么做了,嗯。

run 里的 reduce phase 组装一下参数,并且和上面 map phase 差不多,拼出相应的 task 结构,然后提交任务给worker 就行:

// reduce phase
	startTime := time.Now()
	tasks = make([]*task, 0, nReduce)
	for i := 0; i < nReduce; i++ {
		t := &task{
			dataDir:    dataDir,
			jobName:    jobName,
			phase:      reducePhase,
			taskNumber: i,
			nReduce:    nReduce,
			nMap:       nMap,
			reduceF:    reduceF,
		}
		t.wg.Add(1)
		tasks = append(tasks, t)
		go func() { c.taskCh <- t }()
	}
    
	// 任务收集阶段,需要把最终 merge 到的 filename 传出
	// 这里稍微有一些疑惑
	notifyList := []string{}
	for _, t := range tasks {
		t.wg.Wait()
		mergefileName := mergeName(t.dataDir, t.jobName, t.taskNumber)
		notifyList = append(notifyList, mergefileName)
	}
	du := time.Now().Sub(startTime)
	fmt.Println("reduce time: ", du)

	notify <- notifyList

这个 notifyList 的操作真是猜的,没有任何描述,外部的 example test 写的也比较奇怪,inputfiles 鬼知道在说什么啊。

然后是 worker 里的代码就参考这个 commit 吧。

补完 example,跑 test 直接超过了 10min,test 被 kill 掉了。。汗,感觉出题人这里还是出的有点问题,example 阶段本意是随便补补,能跑通就算过的。现在在稍微挫一点的电脑上,这个 test 10 分钟根本跑不完。在自己家牛逼的 2018 mbp 上试了一下 4 分钟就跑完了。出题人的电脑果然是好电脑,嗯。

原本 map reduce 两个阶段采用的序列化是标准库中的 encoding/json,用火焰图看 cpuprofile 可以看到基本 40+ 都在 json.Marshal,json.Unmarshal。在不修改序列化方式的前提下,最简单的优化就是换个库,用我们陶师傅写的 jsoniter。

跑跑 benchmark,嗯,快了几分钟。但是 cpu 消耗的大头还是在 json 编码解码上,用最粗暴的简单字符串连接,可以尝试一下这里的性能优化极限,基本可以把原来的整体时间缩短一半。仔细想想 apache 生态下那些大数据计算框架,基本都有特殊的协议(avro、pb 什么的)对数据进行压缩,所以用 json 之类的会慢倒也不奇怪了。到达优化极限之后,比较突出的问题就是 runtime.slicebytetoarray 之类的问题了,这明显是因为 map reduce 框架和 mapF 以及 reduceF 的函数签名中数据类型不匹配导致数据总是要从 string 转成 []byte,从 []byte 转成string 而带来的问题,优化也比较简单,能用 []byte 的地方全用 []byte 就行了,减少数据类型转换。

题目还要求对 mapF 和 reduceF 的实现进行优化,round1 其实没什么好优化的,题目里提到 case 的数据本身可能有倾斜,但实际经过实验,在 round1 加入 map 输出 url 和统计数,确实对数据倾斜有极大的优化,但因为 map 的问题,在倾斜不严重的数据集上跑反而更慢了。汗。所以还是要稍微权衡一下。round2 的运行时间本来也不太长,可以在 map 阶段直接对每个 map 任务提前求 top 10,这样 reduce 阶段需要进行的任务就轻了很多,实际优化效果很好,能把 reduce 从几百毫秒优化到几 ms 甚至几十 us,但没啥用,round2 的 reduce 阶段在全局时间中的占比非常少。

这周的 homework 做完还是有点虚的。。可能是需要做之前先看看 map reduce 的论文,就不会在理解这个框架上有磕绊了?大概吧。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK