优化工作流

建立评估指标(eg. Latency) → 定位瓶颈(一般都会定位到某个局部) → 寻找局部解决问题方案 → 尝试方案

不断重复

问题定位工具

pprof

基本原理:

The builtin Go CPU profiler uses the setitimer(2) system call to ask the operating system to be sent a SIGPROF signal 100 times a second. Each signal stops the Go process and gets delivered to a random thread’s sigtrampgo() function. This function then proceeds to call sigprof() or sigprofNonGo() to record the thread’s current stack.

Since Go uses non-blocking I/O, Goroutines that wait on I/O are parked and not running on any threads. Therefore they end up being largely invisible to Go’s builtin CPU profiler.

每秒被唤醒 100 次,记录每个线程上的栈,那些等待 IO 被 gopark 之类挂起的 goroutine 不会被采集到,因为不在线程上运行,gopark 挂起 goroutine 后,当前线程一般会进 schedule → findrunnable 的调度循环。

fgprof

fgprof is implemented as a background goroutine that wakes up 99 times per second and calls runtime.GoroutineProfile. This returns a list of all goroutines regardless of their current On/Off CPU scheduling status and their call stacks.

比较类似,但是会包含那些 Off CPU 的 goroutine。比如可以结合该库与 goroutine 的增长情况来做一段逻辑:当 goroutine 突然增长时,用 fgprof 采样 x 秒,可以发现是在代码的什么位置发生了阻塞。当然,也可以直接把 pprof 的 goroutine stack 给 dump 下来。

trace

一般用来诊断一些诡异的抖动问题,或 runtime 的 bug(或者用来学习 runtime 的执行流),用来做问题诊断效果一般。

基本原理是在 runtime 中埋了大量点,记录一堆 event 来追踪 runtime 执行流程。

如果对一些调度问题有疑问,可以在 trace 里做观察,不过拿来定位问题还是比较费劲的。

一个和 RLock 有关的小故事

perf

perf 也是可以用的,比如线上没开 pprof 的时候,发现 CPU 炸了,perf 可以看看到底在干啥,因为 Go 默认会把 DWARF 调试信息带进二进制文件中,通过 perf 的 zoom 功能也可以一直看到哪行代码(或者是汇编)占用了比较高的 CPU。

$ perf stat -e task-clock,cycles,instructions,cache-references,cache-misses ./hello
yyyyPerformance counter stats for './hello':1.464376      task-clock (msec)         #    0.979 CPUs utilized3,681,288      cycles                    #    2.514 GHz1,722,170      instructions              #    0.47  insn per cycle46,475      cache-references          #   31.737 M/sec21,479      cache-misses              #   46.216 % of all cache refs0.001495925 seconds time elapsed

perf top

局部优化

go test -bench=. -benchmem

或者

go test -cpuprofile -bench

memprofile 同理,一次只 bench 一种,否则可能不准。

全局优化

寻找程序的整体瓶颈。

wrk、pprof、压测平台

https://github.com/bojand/ghz

有压测平台是最好的,方便 AB,自己玩比较容易手忙脚乱,数据错位(压测的时候收集数据写报告经常容易张冠李戴,导致返工,还是有平台安逸)。

性能瓶颈举例

业务逻辑

调用外部命令

package mainimport ("os/exec""testing"uuid "github.com/satori/go.uuid"
)var uu []byte
var u1 uuid.UUIDfunc BenchmarkUUIDExec(b *testing.B) {for i := 0; i < b.N; i++ {uu, _ = exec.Command("uuidgen").Output()}
}func BenchmarkUUIDLib(b *testing.B) {for i := 0; i < b.N; i++ {u1 = uuid.NewV4()}
}

序列化 CPU 占用过高

寻找一些针对性进行过优化的库,或者从文本协议更换为二进制协议。

比如 k8s 为了性能就集成了 jsoniter。

算法时间复杂度

显而易见,O(logn) 和 O(n),O(logn) 最多就 64 次,而 O(n) 可能耗尽计算资源。

runtime 里的算法优化:

过多的系统调用

合并调用

  • 如 writev,但是合并的 syscall 延迟可能会上升。

  • pipeline,一下发一堆请求,不过现在可能连 HTTP 的 pipeline 都不一定支持得好。经常被 benchmark 玩家用来刷数据。

过多的对象

字符串操作

用加号连接,和 Sprintf 差别还是比较大的:

func BenchmarkBytesBufferAppend(b *testing.B) {for i := 0; i < b.N; i++ {var msg bytes.Buffermsg.WriteString("userid : " + "1")msg.WriteString("location : " + "ab")}
}func BenchmarkBytesBufferAppendSprintf(b *testing.B) {for i := 0; i < b.N; i++ {var msg bytes.Buffermsg.WriteString(fmt.Sprintf("userid : %d", 1))msg.WriteString(fmt.Sprintf("location : %s", "ab"))}
}

fmt.打印系列大部分会造成变量逃逸(interface 参数)。

sync.Pool

sync.Pool 才能实现 zero garbage。benchmark 中的 0 alloc,其实是因为对象有复用,alloc 平均 < 1。

struct 可以复用(p = Person{},用零值覆盖一次就可以),slice 可以复用(a = a[:0]),但 map 不太好复用(得把所有 kv 全清空才行,成本可能比新建一个还要高)。比如 fasthttp 里,把本来应该是 map 的 header 结构变成了 slice,牺牲一点查询速度,换来了复用的方便。

复用本身可能导致 bug,例如:

  • 拿出时不 Reset,内含脏数据:

  • slice 缩容时,被缩掉对象如果不置 nil,是不会释放的

  • 在 Put 回 Pool 时,不判断大小,导致了进程占内存越来越大(标准库发生过这样的问题,在用户看起来,整个进程占用的内存一直在上涨,像是泄露一样)

第二点可以看下面这张图理解一下:

a = a[:1],如果后面的元素都是指针,都指向了 500MB 的一个大 buffer,没法释放,GC 认为你还是持有引用的。这种情况需要自己先把后面的元素全置为 nil,再缩容。

offheap

如果数据不可变,只作查询,也可以考虑 offheap,但局限性较大。

下面三个库可以看看。

https://github.com/glycerine/offheap

https://github.com/coocood/freecache

https://github.com/allegro/bigcache

最近 dgraph 有一篇[分享](https://dgraph.io/blog/post/manual-memory-management-golang-jemalloc/),用 jemalloc 和封装的 cgo 方法,可以把一些 hotpath 上分配的对象放在堆外,这个库的局限是在堆外分配的对象不能引用任何 Go 内部的对象,否则可能破坏 GC 时的引用关系。

理论上一些 QPS 较低,但每次请求很大的系统,或许可以参考这个库,把 buffer 放在堆外。

减少指针类型变量逃逸

使用 go build -gcflags="-m -m" 来分析逃逸。

如果要分析某个 package 内的逃逸情况,可以打全 package 名,例如 go build -gcflags="-m -m" github.com/cch123/elasticsql

string 类型天然就是带指针的类型,比如一些 cache 服务,有几千万 entry,那么用 string 来做 key 和 value 可能成本就很高。

减少指针的手段:

用值类型代替指针类型,比如:

*int -> struct {value int, isNull bool}string -> struct {value [12]byte, length int)数值类型的 string -> int*Host -> Host

减少逃逸的手段

  • 尽量少用 fmt.Print、fmt.Sprint 系列的函数。

  • 设计函数签名时,参数尽量少用 interface

  • 少用闭包,被闭包引用的局部变量会逃逸到堆上

不过这些也就说说而已,真的每一条都遵循怕是写代码的时候已经疯了。况且 Go 的 defer 只能在函数作用域内运作,为了避免 panic 死锁,很多时候套个闭包的操作还是比较常见的。

map 结构的 128 阈值

key > 128 字节时,indirectkey = true

value > 128 字节时,indirectvalue = true

我们可以用 lldb 来进行简单验证:

package mainimport "fmt"func main() {type P struct {Age [16]int}var a = map[P]int{}a[P{}] = 1fmt.Println(a)
}

在 lldb 中可以看到 indirectkey 为 false。

(lldb) b mapassign
(lldb) p *t
(runtime.maptype) *t = {typ = {size = 0x0000000000000008ptrdata = 0x0000000000000008hash = 2678392653tflag = 2align = 8fieldalign = 8kind = 53alg = 0x0000000001137020gcdata = 0x00000000010cf298str = 26128ptrToThis = 0}key = 0x00000000010a77a0elem = 0x000000000109d180bucket = 0x00000000010aea00hmap = 0x00000000010b4da0keysize = 128  =======> 128 字节indirectkey = false =====> falsevaluesize = 8indirectvalue = falsebucketsize = 1104reflexivekey = trueneedkeyupdate = false
}

现在 lldb 不支持 Golang 了,在 gdb 或者 dlv 里应该也可以看到这个字段。

过多的调度 CPU 占用(例如火焰图中,schedule 有一大条)

类似 fasthttp 的 workerpool。

worker pool in fasthttp

创建好的 goroutine 可以反复使用,并且自己实现可以控制最大的并发 worker 数。

锁冲突

通过阶梯加压,观察 goroutine 的变化趋势。当触发锁瓶颈时,会出现大量等锁的 goroutine。

原因

临界区太大,其中包含系统调用。

有些锁是避免不了的,例如 fs.Write,一定有锁,且该锁在 runtime 内部。

性能敏感场合,全局锁,比如 rand 的全局锁。单机 10w+ QPS 即可能触发该瓶颈(和环境以及程序行为有关)

type lockedSource struct {lk  sync.Mutexsrc Source64
}func (r *lockedSource) Int63() (n int64) {r.lk.Lock()n = r.src.Int63()r.lk.Unlock()return
}func (r *lockedSource) Uint64() (n uint64) {r.lk.Lock()n = r.src.Uint64()r.lk.Unlock()return
}

有些开源库设计是一个 struct 对应一个 sync.Pool,这种时候,如果你不对该 struct 进行复用,就会触发 runtime 中的锁冲突:

参考本文中的第一个案例:

lock contention

解决方案

  • map → sync.Map(读多写少)

  • 换用无锁结构,如 lock free queue、stack 等

  • 分段锁

  • copy on write,业务逻辑允许的前提下,在修改时拷贝一份,再修改

程序局部性

false sharing

时间局部性、空间局部性

var semtable [semTabSize]struct {root semaRootpad  [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte
}
var timers [timersLen]struct {timersBucket// The padding should eliminate false sharing// between timersBucket values.pad [cpu.CacheLinePadSize - unsafe.Sizeof(timersBucket{})%cpu.CacheLinePadSize]byte
}

类似下面的二维数组,怎么遍历更快?

var a = [10000][10000]int{}

在标准库中,考虑到局部性而实现的 sort 的例子:

func quickSort_func(data lessSwap, a, b, maxDepth int) {for b-a > 12 {if maxDepth == 0 {heapSort_func(data, a, b)return}maxDepth--mlo, mhi := doPivot_func(data, a, b)if mlo-a < b-mhi {quickSort_func(data, a, mlo, maxDepth)a = mhi} else {quickSort_func(data, mhi, b, maxDepth)b = mlo}}if b-a > 1 {for i := a + 6; i < b; i++ {if data.Less(i, i-6) {data.Swap(i, i-6)}}insertionSort_func(data, a, b)}
}

true sharing

这时候一般都有锁,所以本质上还是怎么降低锁的粒度。

sync: RWMutex scales poorly with CPU count

timer 性能问题

  • 老版本的 timer 会有高压力下触发不准时问题,且触发大量的 syscall → Go issue 25471

// xiaorui.ccgo1.13% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------84.00   12.007993         459     26148      3874 futex11.43    1.634512         146     11180           nanosleep4.45    0.635987          32     20185           sched_yieldgo1.14% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------58.78    4.837332         174     27770      4662 futex19.50    1.605189         440      3646           nanosleep11.55    0.950730          44     21569           epoll_pwait9.75    0.802715          36     22181           sched_yield:w

优化后,CPU 占用降低,到时不触发的问题也有所改善。更具体的可以参考 这篇文章。

  • 用时间轮实现粗粒度的时间库

可以搜搜大量的 timewheel 库。

ticker 使用时要尤其注意泄露问题,否则程序 CPU 使用会逐渐上涨:

package mainimport ("fmt""time"
)func main() {for {select {case t := <-time.Tick(time.Second * 2):fmt.Println(t, "hello world")}}
}

汇编优化

SIMD 优化,如 math 库。gonum 中也有一些例子。 无法跨平台,如未来碰到国产化需求要上 ARM、龙芯(MIPS) 就尴尬了。

gonum 中的汇编优化

语言本身的一些缺陷

越压越差

runtime 虽然会对 g 结构体进行 cache 和复用,但在瞬时的高峰时,可能产生同时在执行的大量 goroutine,这些 goroutine 会导致:

var (allgs    []*gallglock mutex
)

runtime 中上面的 allgs 这个数组变大,比如你并发产生了 100000 个 goroutine,那么在高峰过后,即使不再服务任何请求,你的进程占用的 CPU 也会比高峰之前多。

package mainimport ("log""net/http"_ "net/http/pprof""time"
)func sayhello(wr http.ResponseWriter, r *http.Request) {}func main() {for i := 0; i < 1000000; i++ {go func() {time.Sleep(time.Second * 10)}()}http.HandleFunc("/", sayhello)err := http.ListenAndServe(":9090", nil)if err != nil {log.Fatal("ListenAndServe:", err)}
}

可以在你的机器上跑跑上面这个程序,用下面的 python 脚本来验证:

import psutil
import timep = psutil.Process(1) # 改成你自己的 pid 就行了while 1:v = str(p.cpu_percent())if "0.0" != v:print(v, time.time())time.sleep(1)

调度和锁

调度 + 锁出问题,难复现,难定位。

比如高压力下,刚拿到锁就被调度走了,且导致大量的其它需要获取锁的 goroutine 阻塞。

Authors of parallel programs have known for decades that performance can suffer badly if a thread is preempted while holding a lock; this is sometimes referred to as inopportune preemption.

不注意造成死循环会让整个进程 hang 住

GC 需要抢占所有 goroutine,老版本的抢占需要用户协程在 morestack 时主动退出。

卡 gcwaiting。

perf top 可解。

物理机负载高时,延迟非线性增长

压力高会导致响应慢,响应慢会导致并发执行的 goroutine 数变多,响应结束后的垃圾变多,同时会导致更高的调度成本和 GC 扫描成本,级联恶化。

调度导致 CPU 密集型业务超时

package mainimport "fmt"
import "golang.org/x/crypto/bcrypt"
import "time"func test(cost int, id int) {startTime := time.Now()code, _ := bcrypt.GenerateFromPassword([]byte("password"), cost)fmt.Println(time.Since(startTime), "END test ", id, code[0])
}func main() {startTime := time.Now()code, _ := bcrypt.GenerateFromPassword([]byte("password"), 15)fmt.Println(time.Since(startTime), "END test ", -1, code[0], "\n")// 修改 4 为不同的值,猜猜结果?for i := 0; i < 4; i++ {go test(15, i)}time.Sleep(1e16)
}

老版本的问题

sync.Pool 在 GC 时全清空

导致在每一轮 GC 后都有延迟抖动,升级 1.13 后长尾请求有改善。

sync.Pool 的设计思路:尽量从本地拿到 cache 对象,拿不到通过无锁 shared 队列去找,还是找不到,全局 lock 找或者生成新的。

这种思路比较类似 L1 → L2 → L3 的多级缓存设计,runtime 的内存分配演进也是类似的思路。

当前问题定位工具的局限性

难以定位抖动问题。

无论 pprof、perf、fgprof、trace 都是人肉触发,抖动时人又不在系统旁边。

这种情况需要 self-aware 的 profile dump 方式来解决问题。

或者向 Google 看齐:

continuous profiling

在生产环境对更细粒度的程序性能做实时监控,方便及时发现、定位、分析问题。

早发现,早治疗,晚发现,成本高。

google 论文

补充知识

怎么阅读 pprof 的文本格式的 dump

下面是 heap profile 的 dump 结果,我们需要重点关心的是里面的几个数值:


94(94 个 inuse objects): 6160384(6MB inuse space) [5828(5828 个 alloc objects): 381943808(381M 的 alloc space)] @ 0x1370f09 0x1397394 0x1396b32 0x1395e65 0x13981ab 0x13a313a 0x13a9c60 0x13a9401 0x13b4125 0x1703b69 0x1703b0b 0x170cdd1 0x170fcb7 0x170f90d 0x174e369 0x1510930 0x150b5a5 0x17ad46c 0x179fc0e 0x17b1958 0xf43e70 0x49b481 _

后面这些指的是程序的代码地址,Go 可以根据这些地址在 .text 段中找到具体的代码位置。

---
profile : heap profile: 1240: 51385648 [50587184: 280604213336] @ heap/1048576
94: 6160384 [5828: 381943808] @ 0x1370f09 0x1397394 0x1396b32 0x1395e65 0x13981ab 0x13a313a 0x13a9c60 0x13a9401 0x13b4125 0x1703b69 0x1703b0b 0x170cdd1 0x170fcb7 0x170f90d 0x174e369 0x1510930 0x150b5a5 0x17ad46c 0x179fc0e 0x17b1958 0xf43e70 0x49b481
#    0x1370f08       gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client/async.NewAsyncClient+0x208                      /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client/async/async_client.go:55
#  0x1397393       gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client.(*ClientPool).newAsyncClient+0x73               /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client/client_pool.go:110
#        0x1396b31       gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client.(*ClientPool).takeClient+0x3f1                  /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client/client_pool.go:66
# 0x1395e64       gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client.(*ClientHandler).takeClient+0x1b4               /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client/client_handler.go:46
#      0x13981a gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client.(*CommandExecutor).do+0x1ba                     /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client/command_executor.go:58
#    0x13a313 gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client.(*TBaseClient).Do+0x99                          /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/oooo/redis-go-client/redis_client.go:73
#        0x13a9c5 gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/client.(*RedisClientInterceptor).Do+0x13f                                        /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/client/redis_client_interceptor.go:29
#      0x13a9400       gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/client.(*RedisClientDelegate).GetTsEx+0x120                                      /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/client/redis_client_delegate.go:33
# 0x13b4124       gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/cachemanager.(*TBaseCacheManager).GetTsEx+0x1c4                                  /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/cachemanager/redis_cache_manager.go:96
#     0x1703b68       gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/cachemanager.(*RefreshableTBaseCacheManager).GetTsEx+0x98                        /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/upstream/zcache/cachemanager/refreshable_redis_cache_manager.go:55
# 0x1703b0a       gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/integrate.(*ZCacheClient).GetObjectLDC+0x3a                                              /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/integrate/zcacheclient.go:67
#       0x170cdd0       gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/session.queryAlipaySessionById+0x790                                                     /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/session/mobile_session_manager.go:42
#       0x170fcb6       gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/session.getAlipaySession+0x186                                                           /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/session/session_manager.go:35
#      0x170f90c       gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/session.InitSession+0x3c                        /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/session/session_manager.go:14
#      0x174e368       gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/handler.(*SessionHandler).HandleIn+0x618                                         /go/src/gitlab.outer.com/ooh-mesh/mosn/pkg/gateway/handler/session_handler.go:32
#      0x151092f       gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/ooh-mesh/sofa-mosng/pkg/gateway.(*DefaultPipeline).RunInHandlers+0xcf   /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/ooh-mesh/sofa-mosng/pkg/gateway/pipeline.go:45
#    0x150b5a4       gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/ooh-mesh/sofa-mosng/pkg/gateway.(*GatewayFilter).OnReceive+0x3c4        /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/gitlab.outer.com/ooh-mesh/sofa-mosng/pkg/gateway/filter.go:89
#      0x17ad46b       gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/proxy.(*downStream).runReceiveFilters+0xdb                                   /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/proxy/streamfilters.go:50
#      0x179fc0d       gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/proxy.(*downStream).receive+0x24d                                            /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/proxy/downstream.go:404
#        0x17b1957       gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/proxy.(*downStream).OnReceive.func1+0xd7                                     /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/proxy/downstream.go:373
#        0xf43e6f        gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/sync.(*workerPool).spawnWorker+0x4f                                          /go/src/gitlab.outer.com/ooh-mesh/mosn/vendor/mosn.io/mosn/pkg/sync/workerpool.go:169

Golang性能优化相关推荐

  1. Golang 性能优化实战

    作者:trumanyan,腾讯 CSIG 后台开发工程师 项目背景 网关服务作为统一接入服务,是大部分服务的统一入口.为了避免成功瓶颈,需要对其进行尽可能地优化.因此,特别总结一下 golang 后台 ...

  2. golang string 加号连接性能慢_面试必备:浅析C#性能优化的若干种方法

    浅析C#性能优化的若干种方法 1.1 垃圾回收 垃圾回收解放了手工管理对象的工作,提高了程序的健壮性,但副作用就是程序代码可能对于对象创建变得随意. 1.1.1 避免不必要的对象创建 由于垃圾回收的代 ...

  3. 【java 性能优化实战】1 理论分析:性能优化,有哪些衡量指标、性能优化的技术手段

    指标是我们衡量很多事物,以及做出行为决策的重要参考.例如在生活中,当你打算买汽车时,会关注很多指标,比如动力性.燃油经济性.制动性.操纵稳定性.平顺性.通过性.排放与噪声等,而这些指标也都有相关的测试 ...

  4. 课时 17-深入理解 etcd:etcd 性能优化实践(陈星宇)

    本文将主要分享以下五方面的内容: etcd 前节课程回顾复习: 理解 etcd 性能: etcd 性能优化 -server 端: etcd 性能优化 -client 端. etcd 前节课程回顾复习 ...

  5. 从零开始入门 K8s | etcd 性能优化实践

    作者 | 陈星宇(宇慕) 阿里云基础技术中台技术专家 本文整理自<CNCF x Alibaba 云原生技术公开课>第 17 讲. 导读:etcd 是容器云平台用于存储关键元信息的组件.阿里 ...

  6. Linux性能优化全景指南

    原文:http://33h.co/kyq4z Part1Linux性能优化1性能优化性能指标高并发和响应快对应着性能优化的两个核心指标:吞吐和延时 图片来自: www.ctq6.cn 应用负载角度:直 ...

  7. IO 密集型服务 性能优化实战记录

    背景 项目背景 Feature 服务作为特征服务,产出特征数据供上游业务使用.服务压力:高峰期 API 模块 10wQPS,计算模块 20wQPS.服务本地缓存机制: 计算模块有本地缓存,且命中率较高 ...

  8. 计算密集型服务 性能优化实战始末

    背景 项目背景 worker 服务数据链路图 worker 服务消费上游数据(工作日高峰期产出速度达近 200 MB/s,节假日高峰期可达 300MB/s 以上),进行中间处理后,写入多个下游.在实践 ...

  9. Go 应用性能优化指北

    假期重温曹大的性能优化文章,收获还是很大的,值得所有 Gopher 精读.无论是理论还是实践,都能从中学到很多. 为什么要做优化 这是一个速度决定一切的时代,我们的生活在不断地数字化,线下的流程依然在 ...

最新文章

  1. Learn OpenGL (十一):光照贴图
  2. 改善C#程序的建议3:在C#中选择正确的集合进行编码
  3. python太慢了-python 读取大文件越来越慢
  4. deepin终端编译c程序_C/C++知识点之Ubuntu / Debian / Deepin等 Sublime Text 3 配置C++环境(一键编译运行,格式化代码)...
  5. 如何把class里的vector结构体memcpy出来_面试官:请说出线程安全的 ArrayList 有哪些,除了Vector...
  6. 3D远方纯动态白云页面源码
  7. 【clickhouse】ClickHouse中的低基数(LowCardinality)类型
  8. 惠普m154a状态页_惠普新品NS—1005w无线智能应用与驱动安装篇
  9. ajax遍历数组对象数组对象数组,javascript - 从ajax json请求中,如何将对象动态添加到数组中,以便我可以遍历它们? - 堆栈内存溢出...
  10. YY创始人体内植入芯片真相调查
  11. retainall的问题
  12. 共享计算机扫描,windows系统下怎么共享扫描仪?
  13. Struts1 页面提交到ActionForm,form为null
  14. JDBC 连接mysql数据库出现 client does not support authen…… update mysql client
  15. 如何在Ubuntu 20.04上安装和使用Docker
  16. c语言出现源文件未编译,dev运行C语言出问题
  17. 触摸屏在环卫车辆上装控制系统的应用
  18. 读周志华《机器学习》第一章有感(白话总结)
  19. easyui使用datagrid时deleteRow删除后行号问题
  20. 搜狗在任务栏的“天猫618炒鸡红包”如何关闭

热门文章

  1. 基于小波分析与深度学习的脑电信号分类(matlab)
  2. Keras LSTM输入的input的特征和时间纬度
  3. 自定义组合键进入u-boot
  4. 英语水平测试项目----总结遇到的问题
  5. 用Excel生成频率分布表及频率分布直方图
  6. cs224n 2019 Lecture 9: Practical Tips for Final Projects
  7. 细粒度 文档图像版面分析
  8. 资料,丰富资料,丰富资料(模式识别......)
  9. ROS系列:第六章 机器人建模
  10. 川土微CA-IF1051 CAN收发器通过AEC-Q100 Grade 1认证