-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
time: Sleep requires ~7 syscalls #25471
Comments
I doubt it will ever go as low as one in general. In a concurrent language like Go, unlike Python, putting a thread to sleep requires locking data structures in the concurrent runtime. Seven may be more than are truly needed, I'm not sure, but time.Sleep cannot be done in only one call to the kernel without blocking the rest of the program's running goroutines. |
time.Sleep
requires ~7 syscalls
What Rob said. Not sure if there's anything to be done here. /cc @rsc |
I'm a bit confused about your hunch that this is due to locking. Futexes use a |
Does the number of syscalls account for the observed CPU usage in |
Yes, it did. As a workaround, the author now disables the 1Hz ticker if there is nothing to do because of the overhead. (Although that's a good idea anyway to prevent unnecessary wake-ups.) (So the overhead is 0.05% of CPU constantly for a 1Hz ticker (on my test machine), which isn't huge — certainly not for servers. It's 43 seconds per day.) |
/cc @ianlancetaylor @aclements @randall77 too |
The high number of futex calls is surprising, for the reasons you pointed out. Most likely what's happening is not lock contention, but rather that the runtime is waking a sleeping thread, though I haven't confirmed that. |
The Go runtime works with a set of timer goroutines sharded by P. The After that, the goroutine that called Meanwhile, the timer goroutine looks at the new timer, decides how long it has to sleep, and goes to sleep for that long: a futex operation. Eventually the timer goroutine wakes up. It unparks the sleeping goroutine, adding it to the queue of goroutines ready to run. There aren't any M's running, so it has to wake one up: a futex operation. The timer goroutine then sees that there are no timers, and goes back to sleep waiting for something to happen: a futex operation. The M that was woken up starts to execute the goroutine, which immediately calls Meanwhile the system monitor thread runs, poking along doing its own thing while all this is happening. On tip this mostly shows up as calls to So I think that gives me 6 system calls per Some important points here are that the system monitor thread is part of the background of a Go process. It doesn't have anything to do with So I think this microbenchmark is not necessarily indicative of the cost of calling All in all it's not obvious to me that there is anything to do here. We really do need three threads: the main thread, the timer goroutine, the system monitor. The main thread and the timer goroutine really do need to communicate, and they really do need to go to sleep during the These system calls are all fast and should not put a significant load on the system. As mentioned above the comparison to Python is unhelpful, since Python doesn't have to support multiplexing multiple sleeping goroutines onto a small set of threads. |
My comparison with Python indeed wasn't fair. Thanks for the explanation. If I understand correctly the current implementation forces the timer goroutine to be executed on a different machine thread than the goroutine calling it. Couldn't the machine thread of the sleeper be reused when there is no |
Given the existence of timers, there has to be a thread that sleeps until the next timer is ready. In the current implementation that thread is the one running the timer goroutine. So in the ordinary steady state there's no way for the goroutine that calls I think it's true that in your example program, in which there is only ever one timer, when the call to |
Is that actually true? I thought that, at least on some platforms, there is a system call you can use to deliver a signal to the process at the next timer instead: if that's true, we only need a sleeping thread if there isn't some other running thread in the process that can receive the signal. |
Sending a signal would not be a great idea--handling signals is not particularly fast--but it's true that on GNU/Linux we could use |
I think it's also worth saying explicitly that this "overhead" of syscalls is only true when the app is essentially idle. For example, if there had been more goroutines ready to run, the M would not have been parked. A polling loop in idle state like in the original is a bad idea anyway; let your CPU sleep when there's nothing to do. It benefits both laptops (battery life) and servers (less power consumed). |
Go 1.14 has a new timer implementation that changes the analysis here somewhat. There is no longer a separate timer goroutine. Timer operations are now folded into network operations for programs that use networking. This seems to reduce the system call overhead for real programs. However, the overall number of system calls for the simple program above has not gone down. It remains dominated by sysmon and scheduler activity rather than by |
Change https://golang.org/cl/235037 mentions this issue: |
Change https://golang.org/cl/232298 mentions this issue: |
diff --git a/perf_opt/index.html b/perf_opt/index.html new file mode 100644 index 0000000..b95389e --- /dev/null +++ b/perf_opt/index.html @@ -0,0 +1,979 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"> +<meta http-equiv="X-UA-Compatible" content="IE=edge"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="generator" content="Asciidoctor 2.0.8.dev"> +<title>Go 语言性能优化</title> +<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"> +<style> +/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */ +/* Uncomment @import statement to use as custom stylesheet */ +/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/ +article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block} +audio,video{display:inline-block} +audio:not([controls]){display:none;height:0} +html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%} +a{background:none} +a:focus{outline:thin dotted} +a:active,a:hover{outline:0} +h1{font-size:2em;margin:.67em 0} +abbr[title]{border-bottom:1px dotted} +b,strong{font-weight:bold} +dfn{font-style:italic} +hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0} +mark{background:#ff0;color:#000} +code,kbd,pre,samp{font-family:monospace;font-size:1em} +pre{white-space:pre-wrap} +q{quotes:"\201C" "\201D" "\2018" "\2019"} +small{font-size:80%} +sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} +sup{top:-.5em} +sub{bottom:-.25em} +img{border:0} +svg:not(:root){overflow:hidden} +figure{margin:0} +fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} +legend{border:0;padding:0} +button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} +button,input{line-height:normal} +button,select{text-transform:none} +button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer} +button[disabled],html input[disabled]{cursor:default} +input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0} +button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} +textarea{overflow:auto;vertical-align:top} +table{border-collapse:collapse;border-spacing:0} +*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} +html,body{font-size:100%} +body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased} +a:hover{cursor:pointer} +img,object,embed{max-width:100%;height:auto} +object,embed{height:100%} +img{-ms-interpolation-mode:bicubic} +.left{float:left!important} +.right{float:right!important} +.text-left{text-align:left!important} +.text-right{text-align:right!important} +.text-center{text-align:center!important} +.text-justify{text-align:justify!important} +.hide{display:none} +img,object,svg{display:inline-block;vertical-align:middle} +textarea{height:auto;min-height:50px} +select{width:100%} +.center{margin-left:auto;margin-right:auto} +.stretch{width:100%} +.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} +div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr} +a{color:#2156a5;text-decoration:underline;line-height:inherit} +a:hover,a:focus{color:#1d4b8f} +a img{border:0} +p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} +p aside{font-size:.875em;line-height:1.35;font-style:italic} +h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} +h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} +h1{font-size:2.125em} +h2{font-size:1.6875em} +h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} +h4,h5{font-size:1.125em} +h6{font-size:1em} +hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0} +em,i{font-style:italic;line-height:inherit} +strong,b{font-weight:bold;line-height:inherit} +small{font-size:60%;line-height:inherit} +code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} +ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} +ul,ol{margin-left:1.5em} +ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em} +ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit} +ul.square{list-style-type:square} +ul.circle{list-style-type:circle} +ul.disc{list-style-type:disc} +ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} +dl dt{margin-bottom:.3125em;font-weight:bold} +dl dd{margin-bottom:1.25em} +abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help} +abbr{text-transform:none} +blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} +blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)} +blockquote cite::before{content:"\2014 \0020"} +blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)} +blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} +@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} +h1{font-size:2.75em} +h2{font-size:2.3125em} +h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} +h4{font-size:1.4375em}} +table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede} +table thead,table tfoot{background:#f7f8f7} +table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} +table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} +table tr.even,table tr.alt{background:#f8f8f7} +table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6} +h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} +h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} +.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table} +.clearfix::after,.float-group::after{clear:both} +:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word} +:not(pre)>code.nobreak{word-wrap:normal} +:not(pre)>code.nowrap{white-space:nowrap} +pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed} +pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit} +pre>code{display:block} +pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal} +em em{font-style:normal} +strong strong{font-weight:400} +.keyseq{color:rgba(51,51,51,.8)} +kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} +.keyseq kbd:first-child{margin-left:0} +.keyseq kbd:last-child{margin-right:0} +.menuseq,.menuref{color:#000} +.menuseq b:not(.caret),.menuref{font-weight:inherit} +.menuseq{word-spacing:-.02em} +.menuseq b.caret{font-size:1.25em;line-height:.8} +.menuseq i.caret{font-weight:bold;text-align:center;width:.45em} +b.button::before,b.button::after{position:relative;top:-1px;font-weight:400} +b.button::before{content:"[";padding:0 3px 0 2px} +b.button::after{content:"]";padding:0 2px 0 3px} +p a>code:hover{color:rgba(0,0,0,.9)} +#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} +#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table} +#header::after,#content::after,#footnotes::after,#footer::after{clear:both} +#content{margin-top:1.25em} +#content::before{content:none} +#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} +#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf} +#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} +#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap} +#header .details span:first-child{margin-left:-.125em} +#header .details span.email a{color:rgba(0,0,0,.85)} +#header .details br{display:none} +#header .details br+span::before{content:"\00a0\2013\00a0"} +#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} +#header .details br+span#revremark::before{content:"\00a0|\00a0"} +#header #revnumber{text-transform:capitalize} +#header #revnumber::after{content:"\00a0"} +#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} +#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em} +#toc>ul{margin-left:.125em} +#toc ul.sectlevel0>li>a{font-style:italic} +#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} +#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} +#toc li{line-height:1.3334;margin-top:.3334em} +#toc a{text-decoration:none} +#toc a:active{text-decoration:underline} +#toctitle{color:#7a2518;font-size:1.2em} +@media screen and (min-width:768px){#toctitle{font-size:1.375em} +body.toc2{padding-left:15em;padding-right:0} +#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} +#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em} +#toc.toc2>ul{font-size:.9em;margin-bottom:0} +#toc.toc2 ul ul{margin-left:0;padding-left:1em} +#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} +body.toc2.toc-right{padding-left:0;padding-right:15em} +body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}} +@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} +#toc.toc2{width:20em} +#toc.toc2 #toctitle{font-size:1.375em} +#toc.toc2>ul{font-size:.95em} +#toc.toc2 ul ul{padding-left:1.25em} +body.toc2.toc-right{padding-left:0;padding-right:20em}} +#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} +#content #toc>:first-child{margin-top:0} +#content #toc>:last-child{margin-bottom:0} +#footer{max-width:100%;background:rgba(0,0,0,.8);padding:1.25em} +#footer-text{color:rgba(255,255,255,.8);line-height:1.44} +#content{margin-bottom:.625em} +.sect1{padding-bottom:.625em} +@media screen and (min-width:768px){#content{margin-bottom:1.25em} +.sect1{padding-bottom:1.25em}} +.sect1:last-child{padding-bottom:0} +.sect1+.sect1{border-top:1px solid #e7e7e9} +#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400} +#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em} +#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible} +#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none} +#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221} +details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em} +details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em} +.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic} +table.tableblock.fit-content>caption.title{white-space:nowrap;width:0} +.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)} +table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit} +.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%} +.admonitionblock>table td.icon{text-align:center;width:80px} +.admonitionblock>table td.icon img{max-width:none} +.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase} +.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)} +.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} +.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px} +.exampleblock>.content>:first-child{margin-top:0} +.exampleblock>.content>:last-child{margin-bottom:0} +.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px} +.sidebarblock>:first-child{margin-top:0} +.sidebarblock>:last-child{margin-bottom:0} +.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} +.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} +.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em} +@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}} +@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}} +.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8} +.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)} +.listingblock>.content{position:relative} +.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5} +.listingblock:hover code[data-lang]::before{display:block} +.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5} +.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"} +.listingblock pre.highlightjs{padding:0} +.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px} +.listingblock pre.prettyprint{border-width:0} +.prettyprint{background:#f7f7f8} +pre.prettyprint .linenums{line-height:1.45;margin-left:2em} +pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0} +pre.prettyprint li code[data-lang]::before{opacity:1} +pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none} +table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none} +table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal} +table.linenotable td.code{padding-left:.75em} +table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em} +pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em} +pre.pygments .lineno::before{content:"";margin-right:-.125em} +.quoteblock{margin:0 1em 1.25em 1.5em;display:table} +.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em} +.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} +.quoteblock blockquote{margin:0;padding:0;border:0} +.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)} +.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0} +.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right} +.verseblock{margin:0 1em 1.25em} +.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility} +.verseblock pre strong{font-weight:400} +.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex} +.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic} +.quoteblock .attribution br,.verseblock .attribution br{display:none} +.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)} +.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none} +.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0} +.quoteblock.abstract{margin:0 1em 1.25em;display:block} +.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center} +.quoteblock.excerpt,.quoteblock .quoteblock{margin:0 0 1.25em;padding:0 0 .25em 1em;border-left:.25em solid #dddddf} +.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem} +.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0} +table.tableblock{max-width:100%;border-collapse:separate} +p.tableblock:last-child{margin-bottom:0} +td.tableblock>.content>:last-child{margin-bottom:-1.25em} +td.tableblock>.content>:last-child.sidebarblock{margin-bottom:0} +table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede} +table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0} +table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0} +table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0} +table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px} +table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0} +table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0} +table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0} +table.frame-all{border-width:1px} +table.frame-sides{border-width:0 1px} +table.frame-topbot,table.frame-ends{border-width:1px 0} +table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7} +th.halign-left,td.halign-left{text-align:left} +th.halign-right,td.halign-right{text-align:right} +th.halign-center,td.halign-center{text-align:center} +th.valign-top,td.valign-top{vertical-align:top} +th.valign-bottom,td.valign-bottom{vertical-align:bottom} +th.valign-middle,td.valign-middle{vertical-align:middle} +table thead th,table tfoot th{font-weight:bold} +tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7} +tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold} +p.tableblock>code:only-child{background:none;padding:0} +p.tableblock{font-size:1em} +ol{margin-left:1.75em} +ul li ol{margin-left:1.5em} +dl dd{margin-left:1.125em} +dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0} +ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em} +ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none} +ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em} +ul.unstyled,ol.unstyled{margin-left:0} +ul.checklist{margin-left:.625em} +ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em} +ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em} +ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em} +ul.inline>li{margin-left:1.25em} +.unstyled dl dt{font-weight:400;font-style:normal} +ol.arabic{list-style-type:decimal} +ol.decimal{list-style-type:decimal-leading-zero} +ol.loweralpha{list-style-type:lower-alpha} +ol.upperalpha{list-style-type:upper-alpha} +ol.lowerroman{list-style-type:lower-roman} +ol.upperroman{list-style-type:upper-roman} +ol.lowergreek{list-style-type:lower-greek} +.hdlist>table,.colist>table{border:0;background:none} +.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none} +td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em} +td.hdlist1{font-weight:bold;padding-bottom:1.25em} +.literalblock+.colist,.listingblock+.colist{margin-top:-.5em} +.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top} +.colist td:not([class]):first-child img{max-width:none} +.colist td:not([class]):last-child{padding:.25em 0} +.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd} +.imageblock.left{margin:.25em .625em 1.25em 0} +.imageblock.right{margin:.25em 0 1.25em .625em} +.imageblock>.title{margin-bottom:0} +.imageblock.thumb,.imageblock.th{border-width:6px} +.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em} +.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0} +.image.left{margin-right:.625em} +.image.right{margin-left:.625em} +a.image{text-decoration:none;display:inline-block} +a.image object{pointer-events:none} +sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super} +sup.footnote a,sup.footnoteref a{text-decoration:none} +sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline} +#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} +#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0} +#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em} +#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em} +#footnotes .footnote:last-of-type{margin-bottom:0} +#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0} +.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0} +.gist .file-data>table td.line-data{width:99%} +div.unbreakable{page-break-inside:avoid} +.big{font-size:larger} +.small{font-size:smaller} +.underline{text-decoration:underline} +.overline{text-decoration:overline} +.line-through{text-decoration:line-through} +.aqua{color:#00bfbf} +.aqua-background{background:#00fafa} +.black{color:#000} +.black-background{background:#000} +.blue{color:#0000bf} +.blue-background{background:#0000fa} +.fuchsia{color:#bf00bf} +.fuchsia-background{background:#fa00fa} +.gray{color:#606060} +.gray-background{background:#7d7d7d} +.green{color:#006000} +.green-background{background:#007d00} +.lime{color:#00bf00} +.lime-background{background:#00fa00} +.maroon{color:#600000} +.maroon-background{background:#7d0000} +.navy{color:#000060} +.navy-background{background:#00007d} +.olive{color:#606000} +.olive-background{background:#7d7d00} +.purple{color:#600060} +.purple-background{background:#7d007d} +.red{color:#bf0000} +.red-background{background:#fa0000} +.silver{color:#909090} +.silver-background{background:#bcbcbc} +.teal{color:#006060} +.teal-background{background:#007d7d} +.white{color:#bfbfbf} +.white-background{background:#fafafa} +.yellow{color:#bfbf00} +.yellow-background{background:#fafa00} +span.icon>.fa{cursor:default} +a span.icon>.fa{cursor:inherit} +.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default} +.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c} +.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111} +.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900} +.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400} +.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000} +.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold} +.conum[data-value] *{color:#fff!important} +.conum[data-value]+b{display:none} +.conum[data-value]::after{content:attr(data-value)} +pre .conum[data-value]{position:relative;top:-.125em} +b.conum *{color:inherit!important} +.conum:not([data-value]):empty{display:none} +dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility} +h1,h2,p,td.content,span.alt{letter-spacing:-.01em} +p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} +p,blockquote,dt,td.content,span.alt{font-size:1.0625rem} +p{margin-bottom:1.25rem} +.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} +.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc} +.print-only{display:none!important} +@page{margin:1.25cm .75cm} +@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important} +html{font-size:80%} +a{color:inherit!important;text-decoration:underline!important} +a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} +a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} +abbr[title]::after{content:" (" attr(title) ")"} +pre,blockquote,tr,img,object,svg{page-break-inside:avoid} +thead{display:table-header-group} +svg{max-width:100%} +p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3} +h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid} +#toc,.sidebarblock,.exampleblock>.content{background:none!important} +#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important} +body.book #header{text-align:center} +body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em} +body.book #header .details{border:0!important;display:block;padding:0!important} +body.book #header .details span:first-child{margin-left:0!important} +body.book #header .details br{display:block} +body.book #header .details br+span::before{content:none!important} +body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important} +body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always} +.listingblock code[data-lang]::before{display:block} +#footer{padding:0 .9375em} +.hide-on-print{display:none!important} +.print-only{display:block!important} +.hide-for-print{display:none!important} +.show-for-print{display:inherit!important}} +@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem} +.sect1{padding:0!important} +.sect1+.sect1{border:0} +#footer{background:none} +#footer-text{color:rgba(0,0,0,.6);font-size:.9em}} +@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}} +</style> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> +</head> +<body class="article"> +<div id="header"> +<h1>Go 语言性能优化</h1> +</div> +<div id="content"> +<div id="preamble"> +<div class="sectionbody"> +<div class="paragraph"> +<p>Latency numbers every programmer should know:</p> +</div> +<div class="paragraph"> +<p><a href="https://colin-scott.github.io/personal_website/research/interactive_latency.html" class="bare">https://colin-scott.github.io/personal_website/research/interactive_latency.html</a></p> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="true优化工作流"><a class="anchor" href="#true优化工作流"></a>优化工作流</h2> +<div class="sectionbody"> +<div class="paragraph"> +<p>建立评估指标(eg. Latency) → 提出解决方案 → 尝试方案</p> +</div> +<div class="paragraph"> +<p>不断重复</p> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="true问题定位工具"><a class="anchor" href="#true问题定位工具"></a>问题定位工具</h2> +<div class="sectionbody"> +<div class="sect2"> +<h3 id="truepprof"><a class="anchor" href="#truepprof"></a>pprof</h3> +<div class="paragraph"> +<p>基本原理:</p> +</div> +<div class="quoteblock"> +<blockquote> +<div class="paragraph"> +<p>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.</p> +</div> +<div class="paragraph"> +<p>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.</p> +</div> +</blockquote> +</div> +<div class="paragraph"> +<p>每秒被唤醒 100 次,记录每个线程上的栈。</p> +</div> +</div> +<div class="sect2"> +<h3 id="truefgprof"><a class="anchor" href="#truefgprof"></a>fgprof</h3> +<div class="quoteblock"> +<blockquote> +<div class="paragraph"> +<p>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.</p> +</div> +</blockquote> +</div> +<div class="paragraph"> +<p>比较类似,但是会包含那些 Off CPU 的 goroutine。</p> +</div> +<div class="paragraph"> +<p>可以用来诊断 CPU、IO 混合的执行时间占比。</p> +</div> +</div> +<div class="sect2"> +<h3 id="truetrace"><a class="anchor" href="#truetrace"></a>trace</h3> +<div class="paragraph"> +<p>一般用来诊断一些诡异的抖动问题,或 runtime 的 bug(或者用来学习 runtime 的执行流),用来做问题诊断效果一般。</p> +</div> +<div class="paragraph"> +<p>基本原理是在 runtime 中埋了大量点,记录一堆 event 来追踪 runtime 执行流程。</p> +</div> +</div> +<div class="sect2"> +<h3 id="trueperf"><a class="anchor" href="#trueperf"></a>perf</h3> +<div class="paragraph"> +<p>perf 也是可以用的。</p> +</div> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="true局部优化"><a class="anchor" href="#true局部优化"></a>局部优化</h2> +<div class="sectionbody"> +<div class="admonitionblock warning"> +<table> +<tr> +<td class="icon"> +<i class="fa icon-warning" title="Warning"></i> +</td> +<td class="content"> +<div class="paragraph"> +<p>调用外部命令</p> +</div> +</td> +</tr> +</table> +</div> +<div class="listingblock"> +<div class="content"> +<pre class="highlightjs highlight"><code data-lang="go" class="language-go hljs">package main + +import ( + "os/exec" + "testing" + + uuid "github.com/satori/go.uuid" +) + +var uu []byte +var u1 uuid.UUID + +func 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() + } +}</code></pre> +</div> +</div> +<div class="admonitionblock warning"> +<table> +<tr> +<td class="icon"> +<i class="fa icon-warning" title="Warning"></i> +</td> +<td class="content"> +<div class="paragraph"> +<p>字符串操作</p> +</div> +</td> +</tr> +</table> +</div> +<div class="paragraph"> +<p>用加号连接,和 Sprintf 差别还是比较大的:</p> +</div> +<div class="listingblock"> +<div class="content"> +<pre class="highlightjs highlight"><code data-lang="go" class="language-go hljs">func BenchmarkBytesBufferAppend(b *testing.B) { + for i := 0; i < b.N; i++ { + var msg bytes.Buffer + msg.WriteString("userid : " + "1") + msg.WriteString("location : " + "ab") + } +} + +func BenchmarkBytesBufferAppendSprintf(b *testing.B) { + for i := 0; i < b.N; i++ { + var msg bytes.Buffer + msg.WriteString(fmt.Sprintf("userid : %d", 1)) + msg.WriteString(fmt.Sprintf("location : %s", "ab")) + } +}</code></pre> +</div> +</div> +<div class="imageblock"> +<div class="content"> +<img src="string_bench.png" alt="string bench"> +</div> +</div> +<div class="paragraph"> +<p>fmt.打印系列大部分会造成变量逃逸(interface 参数)。</p> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="true全局优化"><a class="anchor" href="#true全局优化"></a>全局优化</h2> +<div class="sectionbody"> +<div class="paragraph"> +<p>寻找程序的整体瓶颈。</p> +</div> +<div class="paragraph"> +<p>wrk、pprof、压测平台</p> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="true性能瓶颈举例"><a class="anchor" href="#true性能瓶颈举例"></a>性能瓶颈举例</h2> +<div class="sectionbody"> +<div class="sect2"> +<h3 id="true业务逻辑"><a class="anchor" href="#true业务逻辑"></a>业务逻辑</h3> +<div class="ulist"> +<ul> +<li> +<p>查数据库本来 in 查询就可以了,但是却 for 循环查。 +TODO</p> +</li> +</ul> +</div> +</div> +<div class="sect2"> +<h3 id="true序列化-cpu-占用过高"><a class="anchor" href="#true序列化-cpu-占用过高"></a>序列化 CPU 占用过高</h3> +<div class="paragraph"> +<p>寻找一些针对性进行过优化的库,或者从文本协议更换为二进制协议。</p> +</div> +</div> +<div class="sect2"> +<h3 id="true算法时间复杂度"><a class="anchor" href="#true算法时间复杂度"></a>算法时间复杂度</h3> +<div class="paragraph"> +<p>显而易见,O(logn) 和 O(n),O(logn) 最多就 64 次,而 O(n) 可能耗尽计算资源。</p> +</div> +</div> +<div class="sect2"> +<h3 id="true过多的系统调用"><a class="anchor" href="#true过多的系统调用"></a>过多的系统调用</h3> +<div class="paragraph"> +<p>合并调用,writev?</p> +</div> +</div> +<div class="sect2"> +<h3 id="true过多的对象"><a class="anchor" href="#true过多的对象"></a>过多的对象</h3> +<div class="sect3"> +<h4 id="truesync-pool"><a class="anchor" href="#truesync-pool"></a>sync.Pool</h4> +<div class="paragraph"> +<p>sync.Pool 才能实现 zero garbage。benchmark 中的 0 alloc,其实是因为对象有复用,alloc 平均 < 1。</p> +</div> +<div class="paragraph"> +<p>struct 可以复用,slice 可以复用,但 map 不太好复用。</p> +</div> +<div class="paragraph"> +<p>复用本身可能导致 bug,例如:</p> +</div> +<div class="ulist"> +<ul> +<li> +<p>拿出时不 Reset</p> +</li> +<li> +<p>slice 缩容时,被缩掉对象如果不置 nil,是不会释放的</p> +</li> +<li> +<p>在 Put 回 Pool 时,不判断大小,导致了进程占内存越来越大</p> +</li> +</ul> +</div> +</div> +<div class="sect3"> +<h4 id="trueoffheap"><a class="anchor" href="#trueoffheap"></a>offheap</h4> +<div class="paragraph"> +<p>如果数据不可变,只作查询,也可以考虑 offheap,但局限性较大。</p> +</div> +</div> +<div class="sect3"> +<h4 id="true减少变量逃逸"><a class="anchor" href="#true减少变量逃逸"></a>减少变量逃逸</h4> +<div class="paragraph"> +<p>使用 go build -gcflags="-m -m" 来分析逃逸。</p> +</div> +<div class="paragraph"> +<p>如果要分析某个 package 内的逃逸情况,可以打全 package 名,例如 go build -gcflags="-m -m" github.com/cch123/elasticsql</p> +</div> +</div> +</div> +<div class="sect2"> +<h3 id="true过多的调度-cpu-占用例如火焰图中schedule-有一大条"><a class="anchor" href="#true过多的调度-cpu-占用例如火焰图中schedule-有一大条"></a>过多的调度 CPU 占用(例如火焰图中,schedule 有一大条)</h3> +<div class="paragraph"> +<p>类似 fasthttp 的 workerpool。</p> +</div> +<div class="paragraph"> +<p><a href="https://github.com/valyala/fasthttp/blob/master/workerpool.go#L19" class="bare">https://github.com/valyala/fasthttp/blob/master/workerpool.go#L19</a></p> +</div> +<div class="paragraph"> +<p>创建好的 goroutine 可以反复使用,并且自己实现可以控制最大的并发 worker 数。</p> +</div> +</div> +<div class="sect2"> +<h3 id="true锁冲突"><a class="anchor" href="#true锁冲突"></a>锁冲突</h3> +<div class="paragraph"> +<p>通过阶梯加压,观察 goroutine 的变化趋势。当触发锁瓶颈时,会出现大量等锁的 goroutine。</p> +</div> +<div class="sect3"> +<h4 id="true原因"><a class="anchor" href="#true原因"></a>原因</h4> +<div class="paragraph"> +<p>临界区太大,其中包含系统调用。</p> +</div> +<div class="paragraph"> +<p>有些锁是避免不了的,例如 fs.Write,一定有锁,且该锁在 runtime 内部。</p> +</div> +<div class="paragraph"> +<p>性能敏感场合,全局锁,比如 rand 的全局锁。单机 10w+ QPS 即可能触发该瓶颈(和环境以及程序行为有关)</p> +</div> +<div class="paragraph"> +<p>有些开源库设计是一个 struct 对应一个 sync.Pool,这种时候,如果你不对该 struct 进行复用,就会触发 runtime 中的锁冲突:</p> +</div> +<div class="paragraph"> +<p>TODO</p> +</div> +</div> +<div class="sect3"> +<h4 id="true解决方案"><a class="anchor" href="#true解决方案"></a>解决方案</h4> +<div class="ulist"> +<ul> +<li> +<p>map → sync.Map。</p> +</li> +<li> +<p>换用无锁结构。</p> +</li> +<li> +<p>分段锁</p> +</li> +<li> +<p>copy on write</p> +</li> +</ul> +</div> +</div> +</div> +<div class="sect2"> +<h3 id="true程序局部性"><a class="anchor" href="#true程序局部性"></a>程序局部性</h3> +<div class="sect3"> +<h4 id="truefalse-sharing"><a class="anchor" href="#truefalse-sharing"></a>false sharing</h4> +<div class="paragraph"> +<p>时间局部性、空间局部性</p> +</div> +<div class="exampleblock"> +<div class="content"> +<div class="paragraph"> +<p>var semtable [semTabSize]struct { + root semaRoot + pad [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte +}</p> +</div> +</div> +</div> +<div class="exampleblock"> +<div class="content"> +<div class="paragraph"> +<p>var timers [timersLen]struct { + timersBucket</p> +</div> +<div class="literalblock"> +<div class="content"> +<pre> // The padding should eliminate false sharing + // between timersBucket values. + pad [cpu.CacheLinePadSize - unsafe.Sizeof(timersBucket{})%cpu.CacheLinePadSize]byte +}</pre> +</div> +</div> +</div> +</div> +<div class="paragraph"> +<p>类似下面的二维数组,怎么遍历更快?</p> +</div> +<div class="exampleblock"> +<div class="content"> +<div class="paragraph"> +<p>var a = [10000][10000]int{}</p> +</div> +</div> +</div> +<div class="paragraph"> +<p>在标准库中,考虑到局部性而实现的 sort 的例子:</p> +</div> +<div class="exampleblock"> +<div class="content"> +<div class="paragraph"> +<p>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) + } +}</p> +</div> +</div> +</div> +</div> +<div class="sect3"> +<h4 id="truetrue-sharing"><a class="anchor" href="#truetrue-sharing"></a>true sharing</h4> +<div class="paragraph"> +<p>这时候一般都有锁,所以本质上还是怎么降低锁的粒度。</p> +</div> +<div class="quoteblock"> +<blockquote> +<div class="paragraph"> +<p>sync: RWMutex scales poorly with CPU count</p> +</div> +</blockquote> +</div> +</div> +</div> +<div class="sect2"> +<h3 id="truetimer-性能问题"><a class="anchor" href="#truetimer-性能问题"></a>timer 性能问题</h3> +<div class="ulist"> +<ul> +<li> +<p>老版本的 timer 会有大量的 syscall → <a href="golang/go#25471" class="bare">https://github.com/golang/go/issues/25471</a></p> +</li> +</ul> +</div> +<div class="exampleblock"> +<div class="content"> +<div class="paragraph"> +<p>go1.13</p> +</div> +<div class="paragraph"> +<p>% time seconds usecs/call calls errors syscall +------ ----------- ----------- --------- --------- ---------------- + 84.00 12.007993 459 26148 3874 futex + 11.43 1.634512 146 11180 nanosleep + 4.45 0.635987 32 20185 sched_yield</p> +</div> +<div class="paragraph"> +<p>go1.14</p> +</div> +<div class="paragraph"> +<p>% time seconds usecs/call calls errors syscall +------ ----------- ----------- --------- --------- ---------------- + 58.78 4.837332 174 27770 4662 futex + 19.50 1.605189 440 3646 nanosleep + 11.55 0.950730 44 21569 epoll_pwait + 9.75 0.802715 36 22181 sched_yield:w</p> +</div> +</div> +</div> +<div class="ulist"> +<ul> +<li> +<p>用时间轮实现粗粒度的时间库</p> +</li> +</ul> +</div> +<div class="paragraph"> +<p>可以搜搜大量的 timewheel 库。</p> +</div> +</div> +<div class="sect2"> +<h3 id="true汇编优化"><a class="anchor" href="#true汇编优化"></a>汇编优化</h3> +<div class="paragraph"> +<p>SIMD 优化,如 math 库。gonum 中也有一些例子。 +无法跨平台,如未来碰到国产化需求要上 ARM、龙芯(MIPS) 就尴尬了。</p> +</div> +<div class="paragraph"> +<p><a href="https://github.com/gonum/gonum/tree/master/internal/asm/f64" class="bare">https://github.com/gonum/gonum/tree/master/internal/asm/f64</a></p> +</div> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="true语言本身的一些缺陷"><a class="anchor" href="#true语言本身的一些缺陷"></a>语言本身的一些缺陷</h2> +<div class="sectionbody"> +<div class="sect2"> +<h3 id="true越压越差"><a class="anchor" href="#true越压越差"></a>越压越差</h3> + +</div> +<div class="sect2"> +<h3 id="true调度和锁"><a class="anchor" href="#true调度和锁"></a>调度和锁</h3> +<div class="paragraph"> +<p>调度 + 锁出问题,难复现,难定位</p> +</div> +</div> +<div class="sect2"> +<h3 id="true不注意造成死循环会让整个进程-hang-住"><a class="anchor" href="#true不注意造成死循环会让整个进程-hang-住"></a>不注意造成死循环会让整个进程 hang 住</h3> +<div class="paragraph"> +<p>GC 需要抢占所有 goroutine,老版本的抢占需要用户协程在 morestack 时主动退出。</p> +</div> +<div class="paragraph"> +<p>卡 gcwaiting。</p> +</div> +</div> +<div class="sect2"> +<h3 id="true物理机负载高时延迟非线性增长"><a class="anchor" href="#true物理机负载高时延迟非线性增长"></a>物理机负载高时,延迟非线性增长</h3> +<div class="paragraph"> +<p>TODO</p> +</div> +</div> +<div class="sect2"> +<h3 id="true调度导致-cpu-密集型业务超时"><a class="anchor" href="#true调度导致-cpu-密集型业务超时"></a>调度导致 CPU 密集型业务超时</h3> +<div class="paragraph"> +<p>TODO,bcrypt 的例子</p> +</div> +<div class="paragraph"> +<p>因为调度导致的全部超时</p> +</div> +</div> +<div class="sect2"> +<h3 id="true老版本的问题"><a class="anchor" href="#true老版本的问题"></a>老版本的问题</h3> +<div class="sect3"> +<h4 id="truesync-pool-在-gc-时全清空"><a class="anchor" href="#truesync-pool-在-gc-时全清空"></a>sync.Pool 在 GC 时全清空</h4> +<div class="paragraph"> +<p>导致在每一轮 GC 后都有延迟抖动,升级 1.13 后长尾请求有改善。</p> +</div> +</div> +</div> +<div class="sect2"> +<h3 id="true当前问题定位工具的局限性"><a class="anchor" href="#true当前问题定位工具的局限性"></a>当前问题定位工具的局限性</h3> +<div class="paragraph"> +<p>难以定位抖动问题。</p> +</div> +<div class="paragraph"> +<p>无论 pprof、perf、fgprof、trace 都是人肉触发,抖动时人又不在系统旁边。</p> +</div> +<div class="paragraph"> +<p>这种情况需要 self-aware 的 profile dump 方式来解决问题。</p> +</div> +<div class="paragraph"> +<p>或者向 Google 看齐:</p> +</div> +<div class="sect3"> +<h4 id="truecontinuous-profiling"><a class="anchor" href="#truecontinuous-profiling"></a>continuous profiling</h4> +<div class="paragraph"> +<p>早发现,早治疗。</p> +</div> +</div> +</div> +</div> +</div> +</div> +<div id="footer"> +<div id="footer-text"> +Last updated 2020-09-10 01:34:10 +0800 +</div> +</div> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/github.min.css"> +<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/highlight.min.js"></script> +<script>hljs.initHighlighting()</script> +</body> +</html>
diff --git a/perf_opt/con_perf.png b/perf_opt/con_perf.png new file mode 100644 index 0000000..4073e76 Binary files /dev/null and b/perf_opt/con_perf.png differ diff --git a/perf_opt/index.adoc b/perf_opt/index.adoc index 6bb06e5..203c920 100644 --- a/perf_opt/index.adoc +++ b/perf_opt/index.adoc @@ -23,7 +23,7 @@ The builtin Go CPU profiler uses the setitimer(2) system call to ask the operati 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 次,记录每个线程上的栈。 +每秒被唤醒 100 次,记录每个线程上的栈,那些等待 IO 被 gopark 之类挂起的 goroutine 不会被采集到,因为不在线程上运行,gopark 挂起 goroutine 后,当前线程一般会进 schedule -> findrunnable 的调度循环。 === fgprof @@ -36,15 +36,41 @@ ____ 可以用来诊断 CPU、IO 混合的执行时间占比。 +这个成本还是比普通的 pprof 高一些的。 + === trace 一般用来诊断一些诡异的抖动问题,或 runtime 的 bug(或者用来学习 runtime 的执行流),用来做问题诊断效果一般。 基本原理是在 runtime 中埋了大量点,记录一堆 event 来追踪 runtime 执行流程。 +如果对一些调度问题有疑问,可以在 trace 里做观察,不过拿来定位问题还是比较费劲的。 + +https://xargin.com/a-rlock-story/ + === perf -perf 也是可以用的。 +perf 也是可以用的,比如线上没开 pprof 的时候,发现 CPU 炸了,perf 可以看看到底在干啥,因为 Go 默认会把 DWARF 调试信息带进二进制文件中,通过 perf 的 zoom 功能也可以一直看到哪行代码(或者是汇编)占用了比较高的 CPU。 + +[source, text] +---- +$ perf stat -e task-clock,cycles,instructions,cache-references,cache-misses ./hello +yyyy + + Performance counter stats for './hello': + + 1.464376 task-clock (msec) # 0.979 CPUs utilized + 3,681,288 cycles # 2.514 GHz + 1,722,170 instructions # 0.47 insn per cycle + 46,475 cache-references # 31.737 M/sec + 21,479 cache-misses # 46.216 % of all cache refs + + 0.001495925 seconds time elapsed +---- + +perf top + +image::perf.png[] == 局部优化 @@ -131,6 +157,46 @@ TODO 显而易见,O(logn) 和 O(n),O(logn) 最多就 64 次,而 O(n) 可能耗尽计算资源。 +runtime 里的算法优化: + +``` + ┌──────────────────────┐ + │ │ + │ │ + │ npagesKey: 130 │ + │ spanKey: 0x1234567 │ + │ priority: 1 │ + │ │ + │ │ + └──────────────────────┘ + │ + ┌────────────────────┴──────────────────┐ + │ │ + ▼ ▼ +┌──────────────────────┐ ┌──────────────────────┐ +│ │ │ │ +│ │ │ │ +│ npagesKey: 129 │ │ npagesKey: 132 │ +│ spanKey: 0x4231560 │ │ spanKey: 0x2234521 │ +│ priority: 10 │ │ priority: 12 │ +│ │ │ │ +│ │ │ │ +└──────────────────────┘ └──────────────────────┘ + │ + ┌───────────┴───────────────────┐ + │ │ + ▼ ▼ + ┌──────────────────────┐ ┌──────────────────────┐ + │ │ │ │ + │ │ │ │ + │ npagesKey: 132 │ │ npagesKey: 136 │ + │ spanKey: 0x2234000 │ │ spanKey: 0x2314213 │ + │ priority: 14 │ │ priority: 131 │ + │ │ │ │ + │ │ │ │ + └──────────────────────┘ └──────────────────────┘ +``` + === 过多的系统调用 合并调用,writev? @@ -153,6 +219,14 @@ struct 可以复用,slice 可以复用,但 map 不太好复用。 如果数据不可变,只作查询,也可以考虑 offheap,但局限性较大。 +下面三个库可以看看。 + +https://github.com/glycerine/offheap + +https://github.com/coocood/freecache + +https://github.com/allegro/bigcache + ==== 减少变量逃逸 使用 go build -gcflags="-m -m" 来分析逃逸。 @@ -181,14 +255,16 @@ https://github.com/valyala/fasthttp/blob/master/workerpool.go#L19 有些开源库设计是一个 struct 对应一个 sync.Pool,这种时候,如果你不对该 struct 进行复用,就会触发 runtime 中的锁冲突: -TODO +参考本文中的第一个案例: + +https://xargin.com/lock-contention-in-go/ ==== 解决方案 -* map -> sync.Map。 -* 换用无锁结构。 +* map -> sync.Map +* 换用无锁结构,如 lock free queue、stack 等 * 分段锁 -* copy on write +* copy on write,业务逻辑允许的前提下,在修改时拷贝一份,再修改 === 程序局部性 @@ -197,15 +273,15 @@ TODO 时间局部性、空间局部性 [source, go] -==== +---- var semtable [semTabSize]struct { root semaRoot pad [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte } -==== +---- [source, go] -==== +---- var timers [timersLen]struct { timersBucket @@ -213,19 +289,19 @@ var timers [timersLen]struct { // between timersBucket values. pad [cpu.CacheLinePadSize - unsafe.Sizeof(timersBucket{})%cpu.CacheLinePadSize]byte } -==== +---- 类似下面的二维数组,怎么遍历更快? [source, go] -==== +---- var a = [10000][10000]int{} -==== +---- 在标准库中,考虑到局部性而实现的 sort 的例子: [source, go] -==== +---- func quickSort_func(data lessSwap, a, b, maxDepth int) { for b-a > 12 { if maxDepth == 0 { @@ -251,7 +327,7 @@ func quickSort_func(data lessSwap, a, b, maxDepth int) { insertionSort_func(data, a, b) } } -==== +---- ==== true sharing @@ -264,9 +340,9 @@ ____ === timer 性能问题 -* 老版本的 timer 会有大量的 syscall -> https://github.com/golang/go/issues/25471 +* 老版本的 timer 会有高压力下触发不准时问题,且触发大量的 syscall -> https://github.com/golang/go/issues/25471 [source, text] -==== +---- // xiaorui.cc go1.13 @@ -286,12 +362,16 @@ go1.14 11.55 0.950730 44 21569 epoll_pwait 9.75 0.802715 36 22181 sched_yield:w -==== +---- + +优化后,CPU 占用降低,到时不触发的问题也有所改善。 * 用时间轮实现粗粒度的时间库 可以搜搜大量的 timewheel 库。 +ticker 使用时要尤其注意泄露问题,否则程序 CPU 使用会逐渐上涨。 + === 汇编优化 SIMD 优化,如 math 库。gonum 中也有一些例子。 @@ -315,7 +395,7 @@ GC 需要抢占所有 goroutine,老版本的抢占需要用户协程在 morest === 物理机负载高时,延迟非线性增长 -TODO +压力高会导致响应慢,响应慢会导致并发执行的 goroutine 数变多,响应结束后的垃圾变多,同时会导致更高的调度成本和 GC 扫描成本,级联恶化。 === 调度导致 CPU 密集型业务超时 @@ -329,6 +409,10 @@ TODO,bcrypt 的例子 导致在每一轮 GC 后都有延迟抖动,升级 1.13 后长尾请求有改善。 +sync.Pool 的设计思路:尽量从本地拿到 cache 对象,拿不到通过无锁 shared 队列去找,还是找不到,全局 lock 找或者生成新的。 + +这种思路比较类似 L1 -> L2 -> L3 的多级缓存设计,runtime 的内存分配演进也是类似的思路。 + === 当前问题定位工具的局限性 难以定位抖动问题。 @@ -341,4 +425,10 @@ TODO,bcrypt 的例子 ==== continuous profiling -早发现,早治疗。 +在生产环境对更细粒度的程序性能做实时监控,方便及时发现、定位、分析问题。 + +早发现,早治疗,晚发现,成本高。 + +https://storage.googleapis.com/pub-tools-public-publication-data/pdf/36575.pdf + +image::con_perf.png[] diff --git a/perf_opt/index.html b/perf_opt/index.html index b95389e..be2a45d 100644 --- a/perf_opt/index.html +++ b/perf_opt/index.html @@ -1,979 +1,1078 @@ -<!DOCTYPE html> -<html lang="en"> -<head> -<meta charset="UTF-8"> -<meta http-equiv="X-UA-Compatible" content="IE=edge"> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> -<meta name="generator" content="Asciidoctor 2.0.8.dev"> -<title>Go 语言性能优化</title> -<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"> -<style> -/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */ -/* Uncomment @import statement to use as custom stylesheet */ -/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/ -article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block} -audio,video{display:inline-block} -audio:not([controls]){display:none;height:0} -html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%} -a{background:none} -a:focus{outline:thin dotted} -a:active,a:hover{outline:0} -h1{font-size:2em;margin:.67em 0} -abbr[title]{border-bottom:1px dotted} -b,strong{font-weight:bold} -dfn{font-style:italic} -hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0} -mark{background:#ff0;color:#000} -code,kbd,pre,samp{font-family:monospace;font-size:1em} -pre{white-space:pre-wrap} -q{quotes:"\201C" "\201D" "\2018" "\2019"} -small{font-size:80%} -sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} -sup{top:-.5em} -sub{bottom:-.25em} -img{border:0} -svg:not(:root){overflow:hidden} -figure{margin:0} -fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} -legend{border:0;padding:0} -button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} -button,input{line-height:normal} -button,select{text-transform:none} -button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer} -button[disabled],html input[disabled]{cursor:default} -input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0} -button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} -textarea{overflow:auto;vertical-align:top} -table{border-collapse:collapse;border-spacing:0} -*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} -html,body{font-size:100%} -body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased} -a:hover{cursor:pointer} -img,object,embed{max-width:100%;height:auto} -object,embed{height:100%} -img{-ms-interpolation-mode:bicubic} -.left{float:left!important} -.right{float:right!important} -.text-left{text-align:left!important} -.text-right{text-align:right!important} -.text-center{text-align:center!important} -.text-justify{text-align:justify!important} -.hide{display:none} -img,object,svg{display:inline-block;vertical-align:middle} -textarea{height:auto;min-height:50px} -select{width:100%} -.center{margin-left:auto;margin-right:auto} -.stretch{width:100%} -.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} -div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr} -a{color:#2156a5;text-decoration:underline;line-height:inherit} -a:hover,a:focus{color:#1d4b8f} -a img{border:0} -p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} -p aside{font-size:.875em;line-height:1.35;font-style:italic} -h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} -h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} -h1{font-size:2.125em} -h2{font-size:1.6875em} -h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} -h4,h5{font-size:1.125em} -h6{font-size:1em} -hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0} -em,i{font-style:italic;line-height:inherit} -strong,b{font-weight:bold;line-height:inherit} -small{font-size:60%;line-height:inherit} -code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} -ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} -ul,ol{margin-left:1.5em} -ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em} -ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit} -ul.square{list-style-type:square} -ul.circle{list-style-type:circle} -ul.disc{list-style-type:disc} -ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} -dl dt{margin-bottom:.3125em;font-weight:bold} -dl dd{margin-bottom:1.25em} -abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help} -abbr{text-transform:none} -blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} -blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)} -blockquote cite::before{content:"\2014 \0020"} -blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)} -blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} -@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} -h1{font-size:2.75em} -h2{font-size:2.3125em} -h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} -h4{font-size:1.4375em}} -table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede} -table thead,table tfoot{background:#f7f8f7} -table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} -table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} -table tr.even,table tr.alt{background:#f8f8f7} -table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6} -h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} -h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} -.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table} -.clearfix::after,.float-group::after{clear:both} -:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word} -:not(pre)>code.nobreak{word-wrap:normal} -:not(pre)>code.nowrap{white-space:nowrap} -pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed} -pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit} -pre>code{display:block} -pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal} -em em{font-style:normal} -strong strong{font-weight:400} -.keyseq{color:rgba(51,51,51,.8)} -kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} -.keyseq kbd:first-child{margin-left:0} -.keyseq kbd:last-child{margin-right:0} -.menuseq,.menuref{color:#000} -.menuseq b:not(.caret),.menuref{font-weight:inherit} -.menuseq{word-spacing:-.02em} -.menuseq b.caret{font-size:1.25em;line-height:.8} -.menuseq i.caret{font-weight:bold;text-align:center;width:.45em} -b.button::before,b.button::after{position:relative;top:-1px;font-weight:400} -b.button::before{content:"[";padding:0 3px 0 2px} -b.button::after{content:"]";padding:0 2px 0 3px} -p a>code:hover{color:rgba(0,0,0,.9)} -#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} -#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table} -#header::after,#content::after,#footnotes::after,#footer::after{clear:both} -#content{margin-top:1.25em} -#content::before{content:none} -#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} -#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf} -#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} -#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap} -#header .details span:first-child{margin-left:-.125em} -#header .details span.email a{color:rgba(0,0,0,.85)} -#header .details br{display:none} -#header .details br+span::before{content:"\00a0\2013\00a0"} -#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} -#header .details br+span#revremark::before{content:"\00a0|\00a0"} -#header #revnumber{text-transform:capitalize} -#header #revnumber::after{content:"\00a0"} -#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} -#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em} -#toc>ul{margin-left:.125em} -#toc ul.sectlevel0>li>a{font-style:italic} -#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} -#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} -#toc li{line-height:1.3334;margin-top:.3334em} -#toc a{text-decoration:none} -#toc a:active{text-decoration:underline} -#toctitle{color:#7a2518;font-size:1.2em} -@media screen and (min-width:768px){#toctitle{font-size:1.375em} -body.toc2{padding-left:15em;padding-right:0} -#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} -#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em} -#toc.toc2>ul{font-size:.9em;margin-bottom:0} -#toc.toc2 ul ul{margin-left:0;padding-left:1em} -#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} -body.toc2.toc-right{padding-left:0;padding-right:15em} -body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}} -@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} -#toc.toc2{width:20em} -#toc.toc2 #toctitle{font-size:1.375em} -#toc.toc2>ul{font-size:.95em} -#toc.toc2 ul ul{padding-left:1.25em} -body.toc2.toc-right{padding-left:0;padding-right:20em}} -#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} -#content #toc>:first-child{margin-top:0} -#content #toc>:last-child{margin-bottom:0} -#footer{max-width:100%;background:rgba(0,0,0,.8);padding:1.25em} -#footer-text{color:rgba(255,255,255,.8);line-height:1.44} -#content{margin-bottom:.625em} -.sect1{padding-bottom:.625em} -@media screen and (min-width:768px){#content{margin-bottom:1.25em} -.sect1{padding-bottom:1.25em}} -.sect1:last-child{padding-bottom:0} -.sect1+.sect1{border-top:1px solid #e7e7e9} -#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400} -#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em} -#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible} -#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none} -#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221} -details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em} -details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em} -.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic} -table.tableblock.fit-content>caption.title{white-space:nowrap;width:0} -.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)} -table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit} -.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%} -.admonitionblock>table td.icon{text-align:center;width:80px} -.admonitionblock>table td.icon img{max-width:none} -.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase} -.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)} -.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} -.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px} -.exampleblock>.content>:first-child{margin-top:0} -.exampleblock>.content>:last-child{margin-bottom:0} -.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px} -.sidebarblock>:first-child{margin-top:0} -.sidebarblock>:last-child{margin-bottom:0} -.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} -.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} -.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em} -@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}} -@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}} -.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8} -.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)} -.listingblock>.content{position:relative} -.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5} -.listingblock:hover code[data-lang]::before{display:block} -.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5} -.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"} -.listingblock pre.highlightjs{padding:0} -.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px} -.listingblock pre.prettyprint{border-width:0} -.prettyprint{background:#f7f7f8} -pre.prettyprint .linenums{line-height:1.45;margin-left:2em} -pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0} -pre.prettyprint li code[data-lang]::before{opacity:1} -pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none} -table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none} -table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal} -table.linenotable td.code{padding-left:.75em} -table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em} -pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em} -pre.pygments .lineno::before{content:"";margin-right:-.125em} -.quoteblock{margin:0 1em 1.25em 1.5em;display:table} -.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em} -.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} -.quoteblock blockquote{margin:0;padding:0;border:0} -.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)} -.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0} -.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right} -.verseblock{margin:0 1em 1.25em} -.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility} -.verseblock pre strong{font-weight:400} -.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex} -.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic} -.quoteblock .attribution br,.verseblock .attribution br{display:none} -.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)} -.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none} -.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0} -.quoteblock.abstract{margin:0 1em 1.25em;display:block} -.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center} -.quoteblock.excerpt,.quoteblock .quoteblock{margin:0 0 1.25em;padding:0 0 .25em 1em;border-left:.25em solid #dddddf} -.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem} -.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0} -table.tableblock{max-width:100%;border-collapse:separate} -p.tableblock:last-child{margin-bottom:0} -td.tableblock>.content>:last-child{margin-bottom:-1.25em} -td.tableblock>.content>:last-child.sidebarblock{margin-bottom:0} -table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede} -table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0} -table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0} -table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0} -table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px} -table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0} -table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0} -table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0} -table.frame-all{border-width:1px} -table.frame-sides{border-width:0 1px} -table.frame-topbot,table.frame-ends{border-width:1px 0} -table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7} -th.halign-left,td.halign-left{text-align:left} -th.halign-right,td.halign-right{text-align:right} -th.halign-center,td.halign-center{text-align:center} -th.valign-top,td.valign-top{vertical-align:top} -th.valign-bottom,td.valign-bottom{vertical-align:bottom} -th.valign-middle,td.valign-middle{vertical-align:middle} -table thead th,table tfoot th{font-weight:bold} -tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7} -tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold} -p.tableblock>code:only-child{background:none;padding:0} -p.tableblock{font-size:1em} -ol{margin-left:1.75em} -ul li ol{margin-left:1.5em} -dl dd{margin-left:1.125em} -dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0} -ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em} -ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none} -ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em} -ul.unstyled,ol.unstyled{margin-left:0} -ul.checklist{margin-left:.625em} -ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em} -ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em} -ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em} -ul.inline>li{margin-left:1.25em} -.unstyled dl dt{font-weight:400;font-style:normal} -ol.arabic{list-style-type:decimal} -ol.decimal{list-style-type:decimal-leading-zero} -ol.loweralpha{list-style-type:lower-alpha} -ol.upperalpha{list-style-type:upper-alpha} -ol.lowerroman{list-style-type:lower-roman} -ol.upperroman{list-style-type:upper-roman} -ol.lowergreek{list-style-type:lower-greek} -.hdlist>table,.colist>table{border:0;background:none} -.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none} -td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em} -td.hdlist1{font-weight:bold;padding-bottom:1.25em} -.literalblock+.colist,.listingblock+.colist{margin-top:-.5em} -.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top} -.colist td:not([class]):first-child img{max-width:none} -.colist td:not([class]):last-child{padding:.25em 0} -.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd} -.imageblock.left{margin:.25em .625em 1.25em 0} -.imageblock.right{margin:.25em 0 1.25em .625em} -.imageblock>.title{margin-bottom:0} -.imageblock.thumb,.imageblock.th{border-width:6px} -.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em} -.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0} -.image.left{margin-right:.625em} -.image.right{margin-left:.625em} -a.image{text-decoration:none;display:inline-block} -a.image object{pointer-events:none} -sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super} -sup.footnote a,sup.footnoteref a{text-decoration:none} -sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline} -#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} -#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0} -#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em} -#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em} -#footnotes .footnote:last-of-type{margin-bottom:0} -#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0} -.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0} -.gist .file-data>table td.line-data{width:99%} -div.unbreakable{page-break-inside:avoid} -.big{font-size:larger} -.small{font-size:smaller} -.underline{text-decoration:underline} -.overline{text-decoration:overline} -.line-through{text-decoration:line-through} -.aqua{color:#00bfbf} -.aqua-background{background:#00fafa} -.black{color:#000} -.black-background{background:#000} -.blue{color:#0000bf} -.blue-background{background:#0000fa} -.fuchsia{color:#bf00bf} -.fuchsia-background{background:#fa00fa} -.gray{color:#606060} -.gray-background{background:#7d7d7d} -.green{color:#006000} -.green-background{background:#007d00} -.lime{color:#00bf00} -.lime-background{background:#00fa00} -.maroon{color:#600000} -.maroon-background{background:#7d0000} -.navy{color:#000060} -.navy-background{background:#00007d} -.olive{color:#606000} -.olive-background{background:#7d7d00} -.purple{color:#600060} -.purple-background{background:#7d007d} -.red{color:#bf0000} -.red-background{background:#fa0000} -.silver{color:#909090} -.silver-background{background:#bcbcbc} -.teal{color:#006060} -.teal-background{background:#007d7d} -.white{color:#bfbfbf} -.white-background{background:#fafafa} -.yellow{color:#bfbf00} -.yellow-background{background:#fafa00} -span.icon>.fa{cursor:default} -a span.icon>.fa{cursor:inherit} -.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default} -.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c} -.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111} -.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900} -.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400} -.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000} -.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold} -.conum[data-value] *{color:#fff!important} -.conum[data-value]+b{display:none} -.conum[data-value]::after{content:attr(data-value)} -pre .conum[data-value]{position:relative;top:-.125em} -b.conum *{color:inherit!important} -.conum:not([data-value]):empty{display:none} -dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility} -h1,h2,p,td.content,span.alt{letter-spacing:-.01em} -p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} -p,blockquote,dt,td.content,span.alt{font-size:1.0625rem} -p{margin-bottom:1.25rem} -.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} -.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc} -.print-only{display:none!important} -@page{margin:1.25cm .75cm} -@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important} -html{font-size:80%} -a{color:inherit!important;text-decoration:underline!important} -a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} -a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} -abbr[title]::after{content:" (" attr(title) ")"} -pre,blockquote,tr,img,object,svg{page-break-inside:avoid} -thead{display:table-header-group} -svg{max-width:100%} -p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3} -h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid} -#toc,.sidebarblock,.exampleblock>.content{background:none!important} -#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important} -body.book #header{text-align:center} -body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em} -body.book #header .details{border:0!important;display:block;padding:0!important} -body.book #header .details span:first-child{margin-left:0!important} -body.book #header .details br{display:block} -body.book #header .details br+span::before{content:none!important} -body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important} -body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always} -.listingblock code[data-lang]::before{display:block} -#footer{padding:0 .9375em} -.hide-on-print{display:none!important} -.print-only{display:block!important} -.hide-for-print{display:none!important} -.show-for-print{display:inherit!important}} -@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem} -.sect1{padding:0!important} -.sect1+.sect1{border:0} -#footer{background:none} -#footer-text{color:rgba(0,0,0,.6);font-size:.9em}} -@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}} -</style> -<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> -</head> -<body class="article"> -<div id="header"> -<h1>Go 语言性能优化</h1> -</div> -<div id="content"> -<div id="preamble"> -<div class="sectionbody"> -<div class="paragraph"> -<p>Latency numbers every programmer should know:</p> -</div> -<div class="paragraph"> -<p><a href="https://colin-scott.github.io/personal_website/research/interactive_latency.html" class="bare">https://colin-scott.github.io/personal_website/research/interactive_latency.html</a></p> -</div> -</div> -</div> -<div class="sect1"> -<h2 id="true优化工作流"><a class="anchor" href="#true优化工作流"></a>优化工作流</h2> -<div class="sectionbody"> -<div class="paragraph"> -<p>建立评估指标(eg. Latency) → 提出解决方案 → 尝试方案</p> -</div> -<div class="paragraph"> -<p>不断重复</p> -</div> -</div> -</div> -<div class="sect1"> -<h2 id="true问题定位工具"><a class="anchor" href="#true问题定位工具"></a>问题定位工具</h2> -<div class="sectionbody"> -<div class="sect2"> -<h3 id="truepprof"><a class="anchor" href="#truepprof"></a>pprof</h3> -<div class="paragraph"> -<p>基本原理:</p> -</div> -<div class="quoteblock"> -<blockquote> -<div class="paragraph"> -<p>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.</p> -</div> -<div class="paragraph"> -<p>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.</p> -</div> -</blockquote> -</div> -<div class="paragraph"> -<p>每秒被唤醒 100 次,记录每个线程上的栈。</p> -</div> -</div> -<div class="sect2"> -<h3 id="truefgprof"><a class="anchor" href="#truefgprof"></a>fgprof</h3> -<div class="quoteblock"> -<blockquote> -<div class="paragraph"> -<p>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.</p> -</div> -</blockquote> -</div> -<div class="paragraph"> -<p>比较类似,但是会包含那些 Off CPU 的 goroutine。</p> -</div> -<div class="paragraph"> -<p>可以用来诊断 CPU、IO 混合的执行时间占比。</p> -</div> -</div> -<div class="sect2"> -<h3 id="truetrace"><a class="anchor" href="#truetrace"></a>trace</h3> -<div class="paragraph"> -<p>一般用来诊断一些诡异的抖动问题,或 runtime 的 bug(或者用来学习 runtime 的执行流),用来做问题诊断效果一般。</p> -</div> -<div class="paragraph"> -<p>基本原理是在 runtime 中埋了大量点,记录一堆 event 来追踪 runtime 执行流程。</p> -</div> -</div> -<div class="sect2"> -<h3 id="trueperf"><a class="anchor" href="#trueperf"></a>perf</h3> -<div class="paragraph"> -<p>perf 也是可以用的。</p> -</div> -</div> -</div> -</div> -<div class="sect1"> -<h2 id="true局部优化"><a class="anchor" href="#true局部优化"></a>局部优化</h2> -<div class="sectionbody"> -<div class="admonitionblock warning"> -<table> -<tr> -<td class="icon"> -<i class="fa icon-warning" title="Warning"></i> -</td> -<td class="content"> -<div class="paragraph"> -<p>调用外部命令</p> -</div> -</td> -</tr> -</table> -</div> -<div class="listingblock"> -<div class="content"> -<pre class="highlightjs highlight"><code data-lang="go" class="language-go hljs">package main - -import ( - "os/exec" - "testing" - - uuid "github.com/satori/go.uuid" -) - -var uu []byte -var u1 uuid.UUID - -func 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() - } -}</code></pre> -</div> -</div> -<div class="admonitionblock warning"> -<table> -<tr> -<td class="icon"> -<i class="fa icon-warning" title="Warning"></i> -</td> -<td class="content"> -<div class="paragraph"> -<p>字符串操作</p> -</div> -</td> -</tr> -</table> -</div> -<div class="paragraph"> -<p>用加号连接,和 Sprintf 差别还是比较大的:</p> -</div> -<div class="listingblock"> -<div class="content"> -<pre class="highlightjs highlight"><code data-lang="go" class="language-go hljs">func BenchmarkBytesBufferAppend(b *testing.B) { - for i := 0; i < b.N; i++ { - var msg bytes.Buffer - msg.WriteString("userid : " + "1") - msg.WriteString("location : " + "ab") - } -} - -func BenchmarkBytesBufferAppendSprintf(b *testing.B) { - for i := 0; i < b.N; i++ { - var msg bytes.Buffer - msg.WriteString(fmt.Sprintf("userid : %d", 1)) - msg.WriteString(fmt.Sprintf("location : %s", "ab")) - } -}</code></pre> -</div> -</div> -<div class="imageblock"> -<div class="content"> -<img src="string_bench.png" alt="string bench"> -</div> -</div> -<div class="paragraph"> -<p>fmt.打印系列大部分会造成变量逃逸(interface 参数)。</p> -</div> -</div> -</div> -<div class="sect1"> -<h2 id="true全局优化"><a class="anchor" href="#true全局优化"></a>全局优化</h2> -<div class="sectionbody"> -<div class="paragraph"> -<p>寻找程序的整体瓶颈。</p> -</div> -<div class="paragraph"> -<p>wrk、pprof、压测平台</p> -</div> -</div> -</div> -<div class="sect1"> -<h2 id="true性能瓶颈举例"><a class="anchor" href="#true性能瓶颈举例"></a>性能瓶颈举例</h2> -<div class="sectionbody"> -<div class="sect2"> -<h3 id="true业务逻辑"><a class="anchor" href="#true业务逻辑"></a>业务逻辑</h3> -<div class="ulist"> -<ul> -<li> -<p>查数据库本来 in 查询就可以了,但是却 for 循环查。 -TODO</p> -</li> -</ul> -</div> -</div> -<div class="sect2"> -<h3 id="true序列化-cpu-占用过高"><a class="anchor" href="#true序列化-cpu-占用过高"></a>序列化 CPU 占用过高</h3> -<div class="paragraph"> -<p>寻找一些针对性进行过优化的库,或者从文本协议更换为二进制协议。</p> -</div> -</div> -<div class="sect2"> -<h3 id="true算法时间复杂度"><a class="anchor" href="#true算法时间复杂度"></a>算法时间复杂度</h3> -<div class="paragraph"> -<p>显而易见,O(logn) 和 O(n),O(logn) 最多就 64 次,而 O(n) 可能耗尽计算资源。</p> -</div> -</div> -<div class="sect2"> -<h3 id="true过多的系统调用"><a class="anchor" href="#true过多的系统调用"></a>过多的系统调用</h3> -<div class="paragraph"> -<p>合并调用,writev?</p> -</div> -</div> -<div class="sect2"> -<h3 id="true过多的对象"><a class="anchor" href="#true过多的对象"></a>过多的对象</h3> -<div class="sect3"> -<h4 id="truesync-pool"><a class="anchor" href="#truesync-pool"></a>sync.Pool</h4> -<div class="paragraph"> -<p>sync.Pool 才能实现 zero garbage。benchmark 中的 0 alloc,其实是因为对象有复用,alloc 平均 < 1。</p> -</div> -<div class="paragraph"> -<p>struct 可以复用,slice 可以复用,但 map 不太好复用。</p> -</div> -<div class="paragraph"> -<p>复用本身可能导致 bug,例如:</p> -</div> -<div class="ulist"> -<ul> -<li> -<p>拿出时不 Reset</p> -</li> -<li> -<p>slice 缩容时,被缩掉对象如果不置 nil,是不会释放的</p> -</li> -<li> -<p>在 Put 回 Pool 时,不判断大小,导致了进程占内存越来越大</p> -</li> -</ul> -</div> -</div> -<div class="sect3"> -<h4 id="trueoffheap"><a class="anchor" href="#trueoffheap"></a>offheap</h4> -<div class="paragraph"> -<p>如果数据不可变,只作查询,也可以考虑 offheap,但局限性较大。</p> -</div> -</div> -<div class="sect3"> -<h4 id="true减少变量逃逸"><a class="anchor" href="#true减少变量逃逸"></a>减少变量逃逸</h4> -<div class="paragraph"> -<p>使用 go build -gcflags="-m -m" 来分析逃逸。</p> -</div> -<div class="paragraph"> -<p>如果要分析某个 package 内的逃逸情况,可以打全 package 名,例如 go build -gcflags="-m -m" github.com/cch123/elasticsql</p> -</div> -</div> -</div> -<div class="sect2"> -<h3 id="true过多的调度-cpu-占用例如火焰图中schedule-有一大条"><a class="anchor" href="#true过多的调度-cpu-占用例如火焰图中schedule-有一大条"></a>过多的调度 CPU 占用(例如火焰图中,schedule 有一大条)</h3> -<div class="paragraph"> -<p>类似 fasthttp 的 workerpool。</p> -</div> -<div class="paragraph"> -<p><a href="https://github.com/valyala/fasthttp/blob/master/workerpool.go#L19" class="bare">https://github.com/valyala/fasthttp/blob/master/workerpool.go#L19</a></p> -</div> -<div class="paragraph"> -<p>创建好的 goroutine 可以反复使用,并且自己实现可以控制最大的并发 worker 数。</p> -</div> -</div> -<div class="sect2"> -<h3 id="true锁冲突"><a class="anchor" href="#true锁冲突"></a>锁冲突</h3> -<div class="paragraph"> -<p>通过阶梯加压,观察 goroutine 的变化趋势。当触发锁瓶颈时,会出现大量等锁的 goroutine。</p> -</div> -<div class="sect3"> -<h4 id="true原因"><a class="anchor" href="#true原因"></a>原因</h4> -<div class="paragraph"> -<p>临界区太大,其中包含系统调用。</p> -</div> -<div class="paragraph"> -<p>有些锁是避免不了的,例如 fs.Write,一定有锁,且该锁在 runtime 内部。</p> -</div> -<div class="paragraph"> -<p>性能敏感场合,全局锁,比如 rand 的全局锁。单机 10w+ QPS 即可能触发该瓶颈(和环境以及程序行为有关)</p> -</div> -<div class="paragraph"> -<p>有些开源库设计是一个 struct 对应一个 sync.Pool,这种时候,如果你不对该 struct 进行复用,就会触发 runtime 中的锁冲突:</p> -</div> -<div class="paragraph"> -<p>TODO</p> -</div> -</div> -<div class="sect3"> -<h4 id="true解决方案"><a class="anchor" href="#true解决方案"></a>解决方案</h4> -<div class="ulist"> -<ul> -<li> -<p>map → sync.Map。</p> -</li> -<li> -<p>换用无锁结构。</p> -</li> -<li> -<p>分段锁</p> -</li> -<li> -<p>copy on write</p> -</li> -</ul> -</div> -</div> -</div> -<div class="sect2"> -<h3 id="true程序局部性"><a class="anchor" href="#true程序局部性"></a>程序局部性</h3> -<div class="sect3"> -<h4 id="truefalse-sharing"><a class="anchor" href="#truefalse-sharing"></a>false sharing</h4> -<div class="paragraph"> -<p>时间局部性、空间局部性</p> -</div> -<div class="exampleblock"> -<div class="content"> -<div class="paragraph"> -<p>var semtable [semTabSize]struct { - root semaRoot - pad [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte -}</p> -</div> -</div> -</div> -<div class="exampleblock"> -<div class="content"> -<div class="paragraph"> -<p>var timers [timersLen]struct { - timersBucket</p> -</div> -<div class="literalblock"> -<div class="content"> -<pre> // The padding should eliminate false sharing - // between timersBucket values. - pad [cpu.CacheLinePadSize - unsafe.Sizeof(timersBucket{})%cpu.CacheLinePadSize]byte -}</pre> -</div> -</div> -</div> -</div> -<div class="paragraph"> -<p>类似下面的二维数组,怎么遍历更快?</p> -</div> -<div class="exampleblock"> -<div class="content"> -<div class="paragraph"> -<p>var a = [10000][10000]int{}</p> -</div> -</div> -</div> -<div class="paragraph"> -<p>在标准库中,考虑到局部性而实现的 sort 的例子:</p> -</div> -<div class="exampleblock"> -<div class="content"> -<div class="paragraph"> -<p>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) - } -}</p> -</div> -</div> -</div> -</div> -<div class="sect3"> -<h4 id="truetrue-sharing"><a class="anchor" href="#truetrue-sharing"></a>true sharing</h4> -<div class="paragraph"> -<p>这时候一般都有锁,所以本质上还是怎么降低锁的粒度。</p> -</div> -<div class="quoteblock"> -<blockquote> -<div class="paragraph"> -<p>sync: RWMutex scales poorly with CPU count</p> -</div> -</blockquote> -</div> -</div> -</div> -<div class="sect2"> -<h3 id="truetimer-性能问题"><a class="anchor" href="#truetimer-性能问题"></a>timer 性能问题</h3> -<div class="ulist"> -<ul> -<li> -<p>老版本的 timer 会有大量的 syscall → <a href="https://github.com/golang/go/issues/25471" class="bare">https://github.com/golang/go/issues/25471</a></p> -</li> -</ul> -</div> -<div class="exampleblock"> -<div class="content"> -<div class="paragraph"> -<p>go1.13</p> -</div> -<div class="paragraph"> -<p>% time seconds usecs/call calls errors syscall ------- ----------- ----------- --------- --------- ---------------- - 84.00 12.007993 459 26148 3874 futex - 11.43 1.634512 146 11180 nanosleep - 4.45 0.635987 32 20185 sched_yield</p> -</div> -<div class="paragraph"> -<p>go1.14</p> -</div> -<div class="paragraph"> -<p>% time seconds usecs/call calls errors syscall ------- ----------- ----------- --------- --------- ---------------- - 58.78 4.837332 174 27770 4662 futex - 19.50 1.605189 440 3646 nanosleep - 11.55 0.950730 44 21569 epoll_pwait - 9.75 0.802715 36 22181 sched_yield:w</p> -</div> -</div> -</div> -<div class="ulist"> -<ul> -<li> -<p>用时间轮实现粗粒度的时间库</p> -</li> -</ul> -</div> -<div class="paragraph"> -<p>可以搜搜大量的 timewheel 库。</p> -</div> -</div> -<div class="sect2"> -<h3 id="true汇编优化"><a class="anchor" href="#true汇编优化"></a>汇编优化</h3> -<div class="paragraph"> -<p>SIMD 优化,如 math 库。gonum 中也有一些例子。 -无法跨平台,如未来碰到国产化需求要上 ARM、龙芯(MIPS) 就尴尬了。</p> -</div> -<div class="paragraph"> -<p><a href="https://github.com/gonum/gonum/tree/master/internal/asm/f64" class="bare">https://github.com/gonum/gonum/tree/master/internal/asm/f64</a></p> -</div> -</div> -</div> -</div> -<div class="sect1"> -<h2 id="true语言本身的一些缺陷"><a class="anchor" href="#true语言本身的一些缺陷"></a>语言本身的一些缺陷</h2> -<div class="sectionbody"> -<div class="sect2"> -<h3 id="true越压越差"><a class="anchor" href="#true越压越差"></a>越压越差</h3> - -</div> -<div class="sect2"> -<h3 id="true调度和锁"><a class="anchor" href="#true调度和锁"></a>调度和锁</h3> -<div class="paragraph"> -<p>调度 + 锁出问题,难复现,难定位</p> -</div> -</div> -<div class="sect2"> -<h3 id="true不注意造成死循环会让整个进程-hang-住"><a class="anchor" href="#true不注意造成死循环会让整个进程-hang-住"></a>不注意造成死循环会让整个进程 hang 住</h3> -<div class="paragraph"> -<p>GC 需要抢占所有 goroutine,老版本的抢占需要用户协程在 morestack 时主动退出。</p> -</div> -<div class="paragraph"> -<p>卡 gcwaiting。</p> -</div> -</div> -<div class="sect2"> -<h3 id="true物理机负载高时延迟非线性增长"><a class="anchor" href="#true物理机负载高时延迟非线性增长"></a>物理机负载高时,延迟非线性增长</h3> -<div class="paragraph"> -<p>TODO</p> -</div> -</div> -<div class="sect2"> -<h3 id="true调度导致-cpu-密集型业务超时"><a class="anchor" href="#true调度导致-cpu-密集型业务超时"></a>调度导致 CPU 密集型业务超时</h3> -<div class="paragraph"> -<p>TODO,bcrypt 的例子</p> -</div> -<div class="paragraph"> -<p>因为调度导致的全部超时</p> -</div> -</div> -<div class="sect2"> -<h3 id="true老版本的问题"><a class="anchor" href="#true老版本的问题"></a>老版本的问题</h3> -<div class="sect3"> -<h4 id="truesync-pool-在-gc-时全清空"><a class="anchor" href="#truesync-pool-在-gc-时全清空"></a>sync.Pool 在 GC 时全清空</h4> -<div class="paragraph"> -<p>导致在每一轮 GC 后都有延迟抖动,升级 1.13 后长尾请求有改善。</p> -</div> -</div> -</div> -<div class="sect2"> -<h3 id="true当前问题定位工具的局限性"><a class="anchor" href="#true当前问题定位工具的局限性"></a>当前问题定位工具的局限性</h3> -<div class="paragraph"> -<p>难以定位抖动问题。</p> -</div> -<div class="paragraph"> -<p>无论 pprof、perf、fgprof、trace 都是人肉触发,抖动时人又不在系统旁边。</p> -</div> -<div class="paragraph"> -<p>这种情况需要 self-aware 的 profile dump 方式来解决问题。</p> -</div> -<div class="paragraph"> -<p>或者向 Google 看齐:</p> -</div> -<div class="sect3"> -<h4 id="truecontinuous-profiling"><a class="anchor" href="#truecontinuous-profiling"></a>continuous profiling</h4> -<div class="paragraph"> -<p>早发现,早治疗。</p> -</div> -</div> -</div> -</div> -</div> -</div> -<div id="footer"> -<div id="footer-text"> -Last updated 2020-09-10 01:34:10 +0800 -</div> -</div> -<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/github.min.css"> -<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/highlight.min.js"></script> -<script>hljs.initHighlighting()</script> -</body> -</html> +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"> +<meta http-equiv="X-UA-Compatible" content="IE=edge"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="generator" content="Asciidoctor 2.0.8.dev"> +<title>Go 语言性能优化</title> +<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"> +<style> +/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */ +/* Uncomment @import statement to use as custom stylesheet */ +/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/ +article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block} +audio,video{display:inline-block} +audio:not([controls]){display:none;height:0} +html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%} +a{background:none} +a:focus{outline:thin dotted} +a:active,a:hover{outline:0} +h1{font-size:2em;margin:.67em 0} +abbr[title]{border-bottom:1px dotted} +b,strong{font-weight:bold} +dfn{font-style:italic} +hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0} +mark{background:#ff0;color:#000} +code,kbd,pre,samp{font-family:monospace;font-size:1em} +pre{white-space:pre-wrap} +q{quotes:"\201C" "\201D" "\2018" "\2019"} +small{font-size:80%} +sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} +sup{top:-.5em} +sub{bottom:-.25em} +img{border:0} +svg:not(:root){overflow:hidden} +figure{margin:0} +fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} +legend{border:0;padding:0} +button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} +button,input{line-height:normal} +button,select{text-transform:none} +button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer} +button[disabled],html input[disabled]{cursor:default} +input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0} +button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} +textarea{overflow:auto;vertical-align:top} +table{border-collapse:collapse;border-spacing:0} +*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} +html,body{font-size:100%} +body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased} +a:hover{cursor:pointer} +img,object,embed{max-width:100%;height:auto} +object,embed{height:100%} +img{-ms-interpolation-mode:bicubic} +.left{float:left!important} +.right{float:right!important} +.text-left{text-align:left!important} +.text-right{text-align:right!important} +.text-center{text-align:center!important} +.text-justify{text-align:justify!important} +.hide{display:none} +img,object,svg{display:inline-block;vertical-align:middle} +textarea{height:auto;min-height:50px} +select{width:100%} +.center{margin-left:auto;margin-right:auto} +.stretch{width:100%} +.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} +div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr} +a{color:#2156a5;text-decoration:underline;line-height:inherit} +a:hover,a:focus{color:#1d4b8f} +a img{border:0} +p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} +p aside{font-size:.875em;line-height:1.35;font-style:italic} +h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} +h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} +h1{font-size:2.125em} +h2{font-size:1.6875em} +h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} +h4,h5{font-size:1.125em} +h6{font-size:1em} +hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0} +em,i{font-style:italic;line-height:inherit} +strong,b{font-weight:bold;line-height:inherit} +small{font-size:60%;line-height:inherit} +code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} +ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} +ul,ol{margin-left:1.5em} +ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em} +ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit} +ul.square{list-style-type:square} +ul.circle{list-style-type:circle} +ul.disc{list-style-type:disc} +ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} +dl dt{margin-bottom:.3125em;font-weight:bold} +dl dd{margin-bottom:1.25em} +abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help} +abbr{text-transform:none} +blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} +blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)} +blockquote cite::before{content:"\2014 \0020"} +blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)} +blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} +@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} +h1{font-size:2.75em} +h2{font-size:2.3125em} +h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} +h4{font-size:1.4375em}} +table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede} +table thead,table tfoot{background:#f7f8f7} +table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} +table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} +table tr.even,table tr.alt{background:#f8f8f7} +table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6} +h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} +h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} +.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table} +.clearfix::after,.float-group::after{clear:both} +:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word} +:not(pre)>code.nobreak{word-wrap:normal} +:not(pre)>code.nowrap{white-space:nowrap} +pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed} +pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit} +pre>code{display:block} +pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal} +em em{font-style:normal} +strong strong{font-weight:400} +.keyseq{color:rgba(51,51,51,.8)} +kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} +.keyseq kbd:first-child{margin-left:0} +.keyseq kbd:last-child{margin-right:0} +.menuseq,.menuref{color:#000} +.menuseq b:not(.caret),.menuref{font-weight:inherit} +.menuseq{word-spacing:-.02em} +.menuseq b.caret{font-size:1.25em;line-height:.8} +.menuseq i.caret{font-weight:bold;text-align:center;width:.45em} +b.button::before,b.button::after{position:relative;top:-1px;font-weight:400} +b.button::before{content:"[";padding:0 3px 0 2px} +b.button::after{content:"]";padding:0 2px 0 3px} +p a>code:hover{color:rgba(0,0,0,.9)} +#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} +#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table} +#header::after,#content::after,#footnotes::after,#footer::after{clear:both} +#content{margin-top:1.25em} +#content::before{content:none} +#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} +#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf} +#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} +#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap} +#header .details span:first-child{margin-left:-.125em} +#header .details span.email a{color:rgba(0,0,0,.85)} +#header .details br{display:none} +#header .details br+span::before{content:"\00a0\2013\00a0"} +#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} +#header .details br+span#revremark::before{content:"\00a0|\00a0"} +#header #revnumber{text-transform:capitalize} +#header #revnumber::after{content:"\00a0"} +#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} +#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em} +#toc>ul{margin-left:.125em} +#toc ul.sectlevel0>li>a{font-style:italic} +#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} +#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} +#toc li{line-height:1.3334;margin-top:.3334em} +#toc a{text-decoration:none} +#toc a:active{text-decoration:underline} +#toctitle{color:#7a2518;font-size:1.2em} +@media screen and (min-width:768px){#toctitle{font-size:1.375em} +body.toc2{padding-left:15em;padding-right:0} +#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} +#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em} +#toc.toc2>ul{font-size:.9em;margin-bottom:0} +#toc.toc2 ul ul{margin-left:0;padding-left:1em} +#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} +body.toc2.toc-right{padding-left:0;padding-right:15em} +body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}} +@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} +#toc.toc2{width:20em} +#toc.toc2 #toctitle{font-size:1.375em} +#toc.toc2>ul{font-size:.95em} +#toc.toc2 ul ul{padding-left:1.25em} +body.toc2.toc-right{padding-left:0;padding-right:20em}} +#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} +#content #toc>:first-child{margin-top:0} +#content #toc>:last-child{margin-bottom:0} +#footer{max-width:100%;background:rgba(0,0,0,.8);padding:1.25em} +#footer-text{color:rgba(255,255,255,.8);line-height:1.44} +#content{margin-bottom:.625em} …
Change the scheduler to treat expired timers with the same approach it uses to steal runnable G's. Previously the scheduler ignored timers on P's not marked for preemption. That had the downside that any G's waiting on those expired timers starved until the G running on their P completed or was preempted. That could take as long as 20ms if sysmon was in a 10ms wake up cycle. In addition, a spinning P that ignored an expired timer and found no other work would stop despite there being available work, missing the opportunity for greater parallelism. With this change the scheduler no longer ignores timers on non-preemptable P's or relies on sysmon as a backstop to start threads when timers expire. Instead it wakes an idle P, if needed, when creating a new timer because it cannot predict if the current P will have a scheduling opportunity before the new timer expires. The P it wakes will determine how long to sleep and block on the netpoller for the required time, potentially stealing the new timer when it wakes. This change also eliminates a race between a spinning P transitioning to idle concurrently with timer creation using the same pattern used for submission of new goroutines in the same window. Benchmark analysis: CL 232199, which was included in Go 1.15 improved timer latency over Go 1.14 by allowing P's to steal timers from P's not marked for preemption. The benchmarks added in this CL measure that improvement in the ParallelTimerLatency benchmark seen below. However, Go 1.15 still relies on sysmon to notice expired timers in some situations and sysmon can sleep for up to 10ms before waking to check timers. This CL fixes that shortcoming with modest regression on other benchmarks. name \ avg-late-ns go14.time.bench go15.time.bench fix.time.bench ParallelTimerLatency-8 17.3M ± 3% 7.9M ± 0% 0.2M ± 3% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=1-8 53.4k ±23% 50.7k ±31% 252.4k ± 9% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=2-8 204k ±14% 90k ±58% 188k ±12% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=3-8 1.17M ± 0% 0.11M ± 5% 0.11M ± 2% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=4-8 1.81M ±44% 0.10M ± 4% 0.10M ± 2% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=5-8 2.28M ±66% 0.09M ±13% 0.08M ±21% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=6-8 2.84M ±85% 0.07M ±15% 0.07M ±18% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=7-8 2.13M ±27% 0.06M ± 4% 0.06M ± 9% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=8-8 2.63M ± 6% 0.06M ±11% 0.06M ± 9% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=9-8 3.32M ±17% 0.06M ±16% 0.07M ±14% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=10-8 8.46M ±20% 4.37M ±21% 5.03M ±23% StaggeredTickerLatency/work-dur=2ms/tickers-per-P=1-8 1.02M ± 1% 0.20M ± 2% 0.20M ± 2% name \ max-late-ns go14.time.bench go15.time.bench fix.time.bench ParallelTimerLatency-8 18.3M ± 1% 8.2M ± 0% 0.5M ±12% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=1-8 141k ±19% 127k ±19% 1129k ± 3% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=2-8 2.78M ± 4% 1.23M ±15% 1.26M ± 5% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=3-8 6.05M ± 5% 0.67M ±56% 0.81M ±33% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=4-8 7.93M ±20% 0.71M ±46% 0.76M ±41% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=5-8 9.41M ±30% 0.92M ±23% 0.81M ±44% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=6-8 10.8M ±42% 0.8M ±41% 0.8M ±30% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=7-8 9.62M ±24% 0.77M ±38% 0.88M ±27% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=8-8 10.6M ±10% 0.8M ±32% 0.7M ±27% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=9-8 11.9M ±36% 0.6M ±46% 0.8M ±38% StaggeredTickerLatency/work-dur=300µs/tickers-per-P=10-8 36.8M ±21% 24.7M ±21% 27.5M ±16% StaggeredTickerLatency/work-dur=2ms/tickers-per-P=1-8 2.12M ± 2% 1.02M ±11% 1.03M ± 7% Other time benchmarks: name \ time/op go14.time.bench go15.time.bench fix.time.bench AfterFunc-8 137µs ± 4% 123µs ± 4% 131µs ± 2% After-8 212µs ± 3% 195µs ± 4% 204µs ± 7% Stop-8 165µs ± 6% 156µs ± 2% 151µs ±12% SimultaneousAfterFunc-8 260µs ± 3% 248µs ± 3% 284µs ± 2% StartStop-8 65.8µs ± 9% 64.4µs ± 7% 67.3µs ±15% Reset-8 13.6µs ± 2% 9.6µs ± 2% 9.1µs ± 4% Sleep-8 307µs ± 4% 306µs ± 3% 320µs ± 2% Ticker-8 53.0µs ± 5% 54.5µs ± 5% 57.0µs ±11% TickerReset-8 9.24µs ± 2% 9.51µs ± 3% TickerResetNaive-8 149µs ± 5% 145µs ± 5% Fixes #38860 Updates #25471 Updates #27707 Change-Id: If52680509b0f3b66dbd1d0c13fa574bd2d0bbd57 Reviewed-on: https://go-review.googlesource.com/c/go/+/232298 Run-TryBot: Alberto Donizetti <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Austin Clements <[email protected]> Trust: Ian Lance Taylor <[email protected]>
What version of Go are you using (
go version
)?go version go1.10.1 linux/amd64
Does this issue reproduce with the latest release?
Yes (
1.10.2
).What operating system and processor architecture are you using (
go env
)?What did you do?
The following Go program calls
time.Sleep
the number of times given as a commandline argument.If track the number of sys calls using
strace -f -c
, we findWhat did you expect to see?
A single
time.Sleep
should use approximately one syscall. (Python'stime.sleep
does only use one syscall, for instance.)What did you see instead?
Approximately seven sys calls per
time.Sleep
. As a consequence, the go process also uses quite a bit of CPU time pertime.Sleep
: 500us (compared to 13us for Python'stime.sleep
).Notes
I encountered this issue while debugging unexpectedly high idle CPU usage by
wireguard-go
.The text was updated successfully, but these errors were encountered: