从回声服务器到网络框架(2)--TimeTrace

在网络服务器开发中,我们对底层的性能是十分看重的。因为只有足够高效的底层支持,才能让你设计更为简单和可靠的服务器框架,或者允许你在应用层更为任性的考虑下Api的便利性。golang本身的benchmark很不错,但是我们还需要一个能在程序正常运行时,实时追踪各个模块性能的工具。这里指的性能,主要是从时间角度上来考虑的,而不是从cpu的利用角度上考虑。我们主要考虑单位时间的吞吐,而不是单位cpu消耗的吞吐。

时间追踪很简单,在需要追踪的开始计算当前时间,在结尾统计中间的消耗。在C++中利用局部对象的自动析构可以很容易做到这一点。在golang中,我们也可以用defer的特性,比较方便的实现。下面是一个简单的追踪器,可以较为方便的追踪某个函数的消耗时间。

它由三部分组成,TraceItem负责计算,TraceManager负责管理,tracevalue负责统计:


type tracevalue struct {
	count      int64
	total_time int64
	min_time   int64
	max_time   int64
}

func (t *tracevalue) add(count int64, total_time int64) {
	atomic.AddInt64(&t.count, count)
	atomic.AddInt64(&t.total_time, total_time)
	if t.min_time > total_time {
		t.min_time = total_time
	}
	if t.max_time < total_time {
		t.max_time = total_time
	}
}

func (t *tracevalue) timeinfo() (min, max, avg int64) {
	if t.count <= 0 {
		return 0, 0, 0
	}
	return t.min_time, t.max_time, t.total_time / t.count
}

func (t *tracevalue) clearn() {
	t.count = 0
	t.total_time = 0
	t.min_time = math.MaxInt64
	t.max_time = math.MinInt64
}

func newTraceValue() *tracevalue {
	ret := &tracevalue{}
	ret.clearn()
	return ret
}


type TraceItem struct {
	t     *TraceManager
	name  string
	begin int64
	end   int64
}

func (i *TraceItem) End() {
	i.end = time.Now().UnixNano()
	if i.t != nil {
		delta := i.end - i.begin
		i.t.Count(i.name, delta)
	}
}

type TraceManager struct {
	values map[string]*tracevalue
	check  time.Duration
	prefix string
	last   time.Time
	sync.Mutex
}

func NewTraceManager(prefix string, check time.Duration) *TraceManager {
	return &TraceManager{prefix: prefix, check: check, values: make(map[string]*tracevalue)}
}

func (t *TraceManager) Begin(tag string) *TraceItem {
	begin := time.Now().UnixNano()
	return &TraceItem{t: t, name: tag, begin: begin}
}

func (t *TraceManager) Count(name string, delta int64) {
	t.Lock()
	defer t.Unlock()

	if old, ok := t.values[name]; ok {
		old.add(1, delta)
		now := time.Now()
		if now.Sub(t.last) > t.check {
			t.last = now
			min, max, avg := old.timeinfo()
			fmt.Println(t.prefix, "trace time with tag:", name, "count:", old.count, "min:", min, "max:", max, "avg:", avg, "in", t.check)
			old.clearn()
		}
	} else {
		new := newTraceValue()
		new.add(1, delta)
		t.values[name] = new
	}
}

使用情况则更为简单, 下面是一个使用的示例:


var FuncTimeTrace = NewTraceManager("FunCall", time.Second)

func ShowToUse() {
	defer FuncTimeTrace.Begin("ShowToUse").End()
	time.Sleep(1 * time.Second)
}

func main() {
	wg := sync.WaitGroup{}
	for c := 0; c <= 1000000; c++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			ShowToUse()
		}()
	}
	wg.Wait()
}

程序输入如下:

FunCall trace time with tag: ShowToUse count: 2 min: 1049395805 max: 1049402780 avg: 1049399292 in 1s
FunCall trace time with tag: ShowToUse count: 480421 min: 1000002405 max: 1133801901 avg: 1005356721 in 1s

讨论请加 QQ group: 549675095


Powered by Jekyll and Theme by solid