<p><code>go get github.com/opennota/check/cmd/aligncheck</code></p>
<p>Invoking <code>aligncheck</code> requires no super powers:</p>
<pre><code>$ aligncheck net/http
net/http: /usr/lib/go/src/net/http/server.go:123:6: struct conn could have size 160 (currently 168)
net/http: /usr/lib/go/src/net/http/server.go:315:6: struct response could have size 152 (currently 176)
net/http: /usr/lib/go/src/net/http/transfer.go:37:6: struct transferWriter could have size 96 (currently 112)
net/http: /usr/lib/go/src/net/http/transport.go:49:6: struct Transport could have size 136 (currently 144)
net/http: /usr/lib/go/src/net/http/transport.go:811:6: struct persistConn could have size 160 (currently 176)
</code></pre>
<p>What it shows are the current sizes of the structs and the minimal sizes that can be achieved if you rearrange the fields.</p>
<p>An example. Let's take the <code>conn</code> struct from <code>net/http</code>:</p>
<pre><code>type conn struct {
remoteAddr string // network address of remote side
server *Server // the Server on which the connection arrived
rwc net.Conn // i/o connection
w io.Writer // checkConnErrorWriter's copy of wrc, not zeroed on Hijack
werr error // any errors writing to w
sr liveSwitchReader // where the LimitReader reads from; usually the rwc
lr *io.LimitedReader // io.LimitReader(sr)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
tlsState *tls.ConnectionState // or nil when not using TLS
lastMethod string // method of previous request, or ""
mu sync.Mutex // guards the following
clientGone bool // if client has disconnected mid-request
closeNotifyc chan bool // made lazily
hijackedv bool // connection has been hijacked by handler
}
</code></pre>
<p>and move the <code>clientGone</code> 1-byte bool field down a step:</p>
<pre><code>type conn struct {
remoteAddr string // network address of remote side
server *Server // the Server on which the connection arrived
rwc net.Conn // i/o connection
w io.Writer // checkConnErrorWriter's copy of wrc, not zeroed on Hijack
werr error // any errors writing to w
sr liveSwitchReader // where the LimitReader reads from; usually the rwc
lr *io.LimitedReader // io.LimitReader(sr)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
tlsState *tls.ConnectionState // or nil when not using TLS
lastMethod string // method of previous request, or ""
mu sync.Mutex // guards the following
closeNotifyc chan bool // made lazily
clientGone bool // if client has disconnected mid-request
hijackedv bool // connection has been hijacked by handler
}
</code></pre>
<p>Now run <code>aligncheck</code> again. It will show you that you're down from 168 to 160 bytes.</p>
<p><a href="https://github.com/opennota/check">The repository.</a></p>
<hr/>**评论:**<br/><br/>infogulch: <pre><p>AFAICT the Go <a href="https://golang.org/ref/spec#Struct_types">spec on structs</a> doesn't guarantee that the physical layout of structs matches the semantic layout in their definition. Therefore, it should be possible for the compiler to rearrange fields during compilation in a way that is transparent to the user.</p>
<p>For example, http.Transport in the playground <a href="https://play.golang.org/p/-yr6RD7sUb">is 104 bytes</a>, much smaller than even what aligncheck suggests is possible. Are we sure the compiler doesn't do this optimization already?</p></pre>codehusker: <pre><p>Running your snippet locally produces 144 bytes. I think 104 bytes is a playground anomaly.</p></pre>dtfinch: <pre><p>The playground compiles to run in Google's <a href="https://github.com/golang/go/wiki/NativeClient">Native Client</a> sandbox. Ints and pointers are smaller on 32-bit.</p></pre>sbinet: <pre><p>I don't know whether gc applies this kind of aggressive optimization but considering that this may break programs using reflect.StructField.Offset, or reflect.Value.Field(i int), it would need to also maintain a set of old/new offsets conversion tables for each type...
sounds wasteful.</p></pre>szabba: <pre><p>How would you expect reflection to work without such information being stored somewhere in the binary?</p></pre>infogulch: <pre><p>reflect.Value.Field "returns the i'th field of the struct v", which should always be the semantic field number. I imagine you can still use <a href="https://golang.org/pkg/reflect/#StructField" rel="nofollow">reflect.StructField</a> with Offset reliably as long as you don't do something stupid like cache the Offset externally and load it in a recompiled process. (edit: also don't make silly assumptions like assuming <code>Field(i).Offset < Field(i+1).Offset</code>)</p>
<p>In addition, I'm pretty sure the behavior of reflect and unsafe packages are not fully covered in the <a href="https://golang.org/doc/go1compat" rel="nofollow">Go 1 Compatibility</a> guarantee.</p></pre>pdq: <pre><p>Does this take into consideration mutexes?</p>
<p>For example, the common Go idiom is that struct member variables below (after) the mutex are guarded behind the mutex. So you would not want to move a variable "across" a mutex.</p>
<p>For example:</p>
<pre><code>aaa bool
mu sync.Mutex // guards the following
clientGone bool // if client has disconnected mid-request
bbb bool
closeNotifyc chan bool // made lazily
hijackedv bool // connection has been hijacked by handler
</code></pre>
<p>In this case you would not want to move aaa next to bbb, even though it may help with packing, because aaa is not a locked/shared member, but bbb is.</p></pre>opennota: <pre><p>No, <code>aligncheck</code> is pretty stupid. I wrote it in an evening on a whim.</p></pre>dchapes: <pre><p>IMO, readability or other reasons for a specific field order should always take precedence over (pre-mature?) optimization of struct space. Only if you have an issue and profiling shows you where (e.g. you allocate a lot of one type of struct) should you worry about trying to save a byte or three by re-ordering.</p></pre>hzck: <pre><p>Does this tool find out about how to rearrange the struct variables or do you have to figure that out yourself?</p></pre>opennota: <pre><p>Not yet.</p></pre>upboatact: <pre><p>I'm using your tools mostly through gometalinter, thanks alot for them, they come in really handy!</p></pre>captncraig: <pre><p>Wasn't there a web visualizer of struct packing somewhere? I liked that, but lost it.
EDIT: found it: <a href="http://golang-sizeof.tips/">http://golang-sizeof.tips/</a></p></pre>Ainar-G: <pre><p>Thanks, looks great!</p>
<p>Obligatory: <a href="http://www.catb.org/esr/structure-packing/" rel="nofollow">http://www.catb.org/esr/structure-packing/</a>. It's about C structs, but AFAIK most of it also applies to Go structs.</p></pre>opennota: <pre><blockquote>
<p>Obligatory: <a href="http://www.catb.org/esr/structure-packing/" rel="nofollow">http://www.catb.org/esr/structure-packing/</a></p>
</blockquote>
<p>Yes.</p></pre>Fwippy: <pre><p>Very cool! Is this architecture-dependent? That is, for best results, should I run it on each architecture that I plan to deploy on?</p></pre>opennota: <pre><blockquote>
<p>Is this architecture-dependent?</p>
</blockquote>
<p>Yes. You can expect different results on 32-bit and 64-bit systems.</p></pre>matttproud: <pre><p>I was just thinking about building a tool to do exactly this. Glad someone beat me to it.</p></pre>michaelKlumpy: <pre><p>can someone eli5 how the arrangement can mess up sizes?<br/>
Does this happen when there are many fractions of an adress?<br/>
32bit 32bit 64bit will use 128bit,<br/>
while 32 bit 64 bit 32bit will use 192bit?</p></pre>szabba: <pre><p>Presumably in a similiar way as they do in C <a href="http://www.catb.org/esr/structure-packing/" rel="nofollow">http://www.catb.org/esr/structure-packing/</a> Not sure if C compilers are allowed to rearrange in-memory locations of the struct fields.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传