structslop: static analyzer for efficient struct packing for Go

Emmanuel Odeke
Nov 6 · 14 min read

TL;DR: at Orijtech, Inc., we’ve developed a first of its kind static analyzer, “structslop” that examines and recommends optimal struct field arrangements in your Go programs; it’ll help you reduce RAM consumed by offending structs, making your programs more efficient! High performance systems require efficiency in every aspect, and our work can help out!

static analysis aided development

Background:

In the Go programming language, and many other languages like ALGOL 68, C, C++, C#, that define the struct type, it is a composite native type that allows composing multiple fields together as an addressable unit; fields are usually built fundamentally from primitive data types, to then build bigger abstractions that are more expressive.

Show me a struct!

For brevity, we shall illustrate with the Go and C programming languages (since they are most relevant to this post)

Programming languages, are tools to interface and express ideas from human programmers into a lower level form that machines can run. To express ideas, let’s for example try to describe a Book collection in Go and in C:

struct in the Go programming language

and in the C programming language

struct in the C programming language

To make the struct declarations more useful, and illustrate how the composition of fields wield power:

In Go:

printbook in Go

In C

printbook in C

If you run those programs you’ll see output from Go and C, respectively as

output from C
output from Go

However, as with everything in life, nothing is free! There is a cost to every declaration and object created. Given that computers are an approximation of Turing Machines but instead with finite memory (not an indefinite tape), and due to limited resources with computing cycles, time, and RAM, we actually have to care for memory overhead. Without being careful of memory, our programs can run out of memory, become uncooperative, get slowed down, and can be killed by the running operating system.

How much does this cost?

The static/raw cost of those structs in Go and C on a 64-bit machine, are 80-bytes and 56-bytes respectively, and you can definitely see that by this code:

For Go:

Go size display

For C:

C size

For Go, we can do better and reduce the memory, by re-arranging it to these this arrangement to 72 bytes, which is a 10% savings as per https://play.golang.org/p/kcCZq9HjsyM or inlined below

How did we do that? How do we scale that for everyone?

Let’s think harder!!

Technical details

Each struct is composed of fields built from other data types and even other structs. In C standards and in Go, the data types have defined types in bytes, for example as per https://golang.org/ref/spec#Size_and_alignment_guarantees

To find the size of objects in Go and C, there are *sizeof functions from the unsafe.Sizeof and the sizeof unary operator respectively. Important rules to note are that pointers are 4-bytes (32 bits) and 8-bytes (64 bits) on 32-bit and 64-bit architecture machines. As for the primitive data types, this table can help us out for example numeric types as per https://golang.org/ref/spec#Numeric_types

Image for post
Image for post
size of numeric types

and then the size of a string is calculated from the underlying parts of how Go stores strings, where to store a string, we need 2 fields:

and this can be examined by looking at reflect.StringHeader as per

Image for post
Image for post
definition of how a string is “seen” by the Go runtime

which can be calculated by: sizeof(Data:uintptr) + sizeof(Len:int)

Then for slices, we can look at how they stored and “seen” by the Go runtime:

and can be seen by looking reflect.SliceHeader as per

Image for post
Image for post
definition of how a slice of any kind is “seen” by the Go runtime

and the sizeof value can be calculated by:

sizeof(Data:uintptr) + sizeof(Len:int) + sizeof(Cap:int)

which is:

Alignment issues!

Given that modern computers use either 32-bit or 64- bit architectures (with 4-byte or 8-byte pointers respectively), they need to be able to fetch data in a single cycle if its size falls within multiples of its pointer sizes; a struct of 73 bytes is not 8 byte aligned because 73 % 8 = 1 in order for the data to be efficiently fetched by your computer’s CPU, it has to be made a multiple of 4-bytes or 8-bytes (32-bit or 64-bit architectures respectively), it’ll need to be padded to that multiple. This means that structs with odd natural sizes will be padded and aligned, hence a bloat. Therefore it is rounded up to the nearest multiple of 8 aka 80

Alignment on its own has other issues, for example in Go you can’t invoke sync/atomic.* methods on fields that aren’t pointer aligned.

Size classes:

Given that garbage collected languages like Go, have to deal with creating a variety of objects, that also means that a callous memory management system can cause wastage of memory, and also cause issues later on when trying to return memory. Such issues have plagued memory allocation systems for decades, and make garbage collection algorithms very hard!

For efficiency with Go’s tcmalloc inspired memory allocator, we have size classes in which objects of a “small size” (size ≤ 32KiB) are rounded up to their closest class for efficiency in allocations.

You can see the size classes in this runtime/sizeclasses.go file. For a quick glimpse, this is what some of the size classes look like

Image for post
Image for post
size classes used by the Go allocator

and for example if a struct is 55 bytes in raw size, it is firstly aligned to its architecture’s pointer size (4B or 8B) so 55 →56 for both (32-bit or 64-bit) and then it’ll look for the ceiling/nearest upper size class which will be class 6 with 64 bytes/obj, thus the struct will be allocated with 64 bytes. This makes it simple to reclaim memory in blocks and reduce memory fragmentation!

Piecing it all together!

Powered by the knowledge about alignment, sizes, size classes, we have built a static analysis pass called “structslop”. Our rationale is that scouring each struct as a human being, and running those calculations per struct is very tedious, and error prone, regardless of how skilled you are. Computers were built meant to solve such curious and laborious tasks for us, and that’s exactly what we’ve done!

Static analysis is a way of finding bugs, improving efficiency without having to execute the program; just by examining the structure of the code we can report defects or improvements. Just like a keen human in code review would look at your struct and point out misalignment issues, structslop will report these problems for you.

Actual savings samples:

Let’s quickly look at some example savings from a variety of Go projects:

Go standard library

The Go programming language is written in Go (self hosted compiler and runtime), and if we can find efficiencies, we can make improvements for every single Go programmer. As of go-tip@b7e0adf, these are some reports from structslop:

go.googlesource.com/go/src/crypto/tls/auth.go:148:29: struct has size 24 (size class 24), could be 16 (size class 16), rearrange to struct{minModulusBytes int; scheme SignatureScheme; maxVersion uint16} for optimal size (33.33% savings)go.googlesource.com/go/src/crypto/tls/cipher_suites.go:25:18: struct has size 56 (size class 64), could be 48 (size class 48), rearrange to struct{SupportedVersions []uint16; Name string; ID uint16; Insecure bool} for optimal size (25.00% savings)go.googlesource.com/go/src/crypto/tls/common.go:333:25: struct has size 232 (size class 240), could be 224 (size class 224), rearrange to struct{sessionTicket []uint8; ocspResponse []byte; scts [][]byte; masterSecret []byte; serverCertificates []*x509.Certificate; verifiedChains [][]*x509.Certificate; receivedAt time.Time; nonce []byte; useBy time.Time; ageAdd uint32; vers uint16; cipherSuite uint16} for optimal size (6.67% savings)go.googlesource.com/go/src/crypto/tls/handshake_messages.go:69:21: struct has size 496 (size class 512), could be 456 (size class 480), rearrange to struct{alpnProtocols []string; pskIdentities []pskIdentity; random []byte; sessionId []byte; cipherSuites []uint16; compressionMethods []uint8; pskModes []uint8; keyShares []keyShare; supportedCurves []CurveID; supportedPoints []uint8; cookie []byte; sessionTicket []uint8; supportedSignatureAlgorithms []SignatureScheme; supportedSignatureAlgorithmsCert []SignatureScheme; supportedVersions []uint16; secureRenegotiation []byte; raw []byte; pskBinders [][]byte; serverName string; vers uint16; ticketSupported bool; ocspStapling bool; earlyData bool; scts bool; secureRenegotiationSupported bool} for optimal size (6.25% savings)go.googlesource.com/go/src/debug/dwarf/line.go:54:16: struct has size 72 (size class 80), could be 64 (size class 64), rearrange to struct{Address uint64; OpIndex int; File *LineFile; Line int; Column int; ISA int; Discriminator int; IsStmt bool; BasicBlock bool; PrologueEnd bool; EpilogueBegin bool; EndSequence bool} for optimal size (20.00% savings)go.googlesource.com/go/src/debug/dwarf/open.go:44:15: struct has size 40 (size class 48), could be 32 (size class 32), rearrange to struct{data []byte; ver uint16; is64 bool; asize uint8} for optimal size (33.33% savings)go.googlesource.com/go/src/encoding/gob/encode.go:26:19: struct has size 56 (size class 64), could be 48 (size class 48), rearrange to struct{enc *Encoder; b *encBuffer; fieldnum int; next *encoderState; buf [9]byte; sendZero bool} for optimal size (25.00% savings)go.googlesource.com/go/src/debug/gosym/pclntab.go:40:16: struct has size 264 (size class 288), could be 256 (size class 256), rearrange to struct{funcnametab []byte; pctab []byte; filetab []byte; functab []byte; funcdata []byte; cutab []byte; Data []byte; binary binary.ByteOrder; Line int; PC uint64; version version; strings map[uint32]string; fileMap map[string]uint32; funcNames map[uint32]string; mu sync.Mutex; ptrsize uint32; quantum uint32; nfiletab uint32; nfunctab uint32} for optimal size (11.11% savings)go.googlesource.com/go/src/crypto/tls/handshake_messages.go:592:21: struct has size 256 (size class 256), could be 232 (size class 240), rearrange to struct{serverShare keyShare; secureRenegotiation []byte; cookie []byte; random []byte; sessionId []byte; supportedPoints []uint8; scts [][]byte; raw []byte; alpnProtocol string; selectedIdentity uint16; vers uint16; supportedVersion uint16; cipherSuite uint16; selectedGroup CurveID; secureRenegotiationSupported bool; compressionMethod uint8; selectedIdentityPresent bool; ticketSupported bool; ocspStapling bool} for optimal size (6.25% savings)go.googlesource.com/go/src/crypto/tls/handshake_server_tls13.go:24:32: struct has size 216 (size class 224), could be 208 (size class 208), rearrange to struct{clientFinished []byte; masterSecret []byte; handshakeSecret []byte; trafficSecret []byte; sharedKey []byte; earlySecret []byte; transcript hash.Hash; c *Conn; cert *Certificate; suite *cipherSuiteTLS13; hello *serverHelloMsg; clientHello *clientHelloMsg; sigAlg SignatureScheme; usingPSK bool; sentDummyCCS bool} for optimal size (7.14% savings)go.googlesource.com/go/src/crypto/tls/ticket.go:22:19: struct has size 72 (size class 80), could be 64 (size class 64), rearrange to struct{masterSecret []byte; certificates [][]byte; createdAt uint64; vers uint16; cipherSuite uint16; usedOldKey bool} for optimal size (20.00% savings)go.googlesource.com/go/src/archive/zip/struct.go:82:17: struct has size 136 (size class 144), could be 128 (size class 128), rearrange to struct{Modified time.Time; Extra []byte; Comment string; Name string; UncompressedSize64 uint64; CompressedSize64 uint64; CRC32 uint32; UncompressedSize uint32; CompressedSize uint32; ExternalAttrs uint32; ModifiedDate uint16; Method uint16; Flags uint16; ReaderVersion uint16; CreatorVersion uint16; ModifiedTime uint16; NonUTF8 bool} for optimal size (11.11% savings)go.googlesource.com/go/src/go/build/read.go:20:19: struct has size 72 (size class 80), could be 64 (size class 64), rearrange to struct{buf []byte; err error; b *bufio.Reader; nerr int; peek byte; eof bool} for optimal size (20.00% savings)go.googlesource.com/go/src/go/doc/reader.go:150:13: struct has size 152 (size class 160), could be 144 (size class 144), rearrange to struct{values []*Value; filenames []string; fixlist []*ast.InterfaceType; doc string; notes map[string][]*Note; imports map[string]int; mode Mode; order int; types map[string]*namedType; funcs methodSet; errorDecl bool; hasDotImp bool} for optimal size (10.00% savings)go.googlesource.com/go/src/go/types/api.go:103:13: struct has size 56 (size class 64), could be 48 (size class 48), rearrange to struct{Importer Importer; Sizes Sizes; Error func(err error); IgnoreFuncBodies bool; FakeImportC bool; go115UsesCgo bool; DisableUnusedImportCheck bool} for optimal size (25.00% savings)go.googlesource.com/go/src/database/sql/sql.go:456:17: struct has size 136 (size class 144), could be 120 (size class 128), rearrange to struct{createdAt time.Time; returnedAt time.Time; onPut []func(); ci driver.Conn; db *DB; openStmt map[*driverStmt]bool; sync.Mutex; closed bool; finalClosed bool; inUse bool; needReset bool; dbmuClosed bool} for optimal size (11.11% savings)go.googlesource.com/go/src/net/dnsconfig_unix.go:23:16: struct has size 152 (size class 160), could be 144 (size class 144), rearrange to struct{mtime time.Time; search []string; servers []string; lookup []string; err error; timeout time.Duration; attempts int; ndots int; soffset uint32; unknownOpt bool; rotate bool; singleRequest bool; useTCP bool} for optimal size (10.00% savings)go.googlesource.com/go/src/net/http/h2_bundle.go:1505:18: struct has size 216 (size class 224), could be 200 (size class 208), rearrange to struct{readBuf []byte; wbuf []byte; errDetail error; w io.Writer; r io.Reader; lastFrame http2Frame; ReadMetaHeaders *hpack.Decoder; getReadBuf func(size uint32) []byte; debugFramer *http2Framer; debugWriteLoggerf func(string, ...interface{}); debugReadLoggerf func(string, ...interface{}); debugFramerBuf *bytes.Buffer; frameCache *http2frameCache; maxReadSize uint32; MaxHeaderListSize uint32; maxWriteSize uint32; lastHeaderStream uint32; headerBuf [9]byte; logReads bool; logWrites bool; AllowIllegalReads bool; AllowIllegalWrites bool} for optimal size (7.14% savings)go.googlesource.com/go/src/net/http/h2_bundle.go:5854:23: struct has size 40 (size class 48), could be 32 (size class 32), rearrange to struct{_ http2incomparable; stream *http2stream; conn *http2serverConn; pipe *http2pipe; closed bool; sawEOF bool; needsContinue bool} for optimal size (33.33% savings)go.googlesource.com/go/src/net/http/h2_bundle.go:6562:21: struct has size 104 (size class 112), could be 96 (size class 96), rearrange to struct{ConnPool http2ClientConnPool; connPoolOrDef http2ClientConnPool; DialTLS func(network string, addr string, cfg *tls.Config) (net.Conn, error); TLSClientConfig *tls.Config; t1 *Transport; ReadIdleTimeout time.Duration; PingTimeout time.Duration; connPoolOnce sync.Once; MaxHeaderListSize uint32; StrictMaxConcurrentStreams bool; DisableCompression bool; AllowHTTP bool} for optimal size (14.29% savings)go.googlesource.com/go/src/net/http/h2_bundle.go:6770:24: struct has size 328 (size class 352), could be 312 (size class 320), rearrange to struct{bufPipe http2pipe; readErr error; inflow http2flow; flow http2flow; resetErr error; stopReqBody error; done chan struct{}; resc chan http2resAndError; on100 func(); trailer Header; trace *httptrace.ClientTrace; bytesRemain int64; req *Request; peerReset chan struct{}; cc *http2ClientConn; resTrailer *Header; ID uint32; didReset bool; firstByte bool; pastHeaders bool; pastTrailers bool; num1xx uint8; requestedGzip bool; startedWrite bool} for optimal size (9.09% savings)go.googlesource.com/go/src/net/http/h2_bundle.go:9319:21: struct has size 40 (size class 48), could be 32 (size class 32), rearrange to struct{p []byte; streamID uint32; endStream bool} for optimal size (33.33% savings)go.googlesource.com/go/src/net/http/h2_bundle.go:9402:27: struct has size 104 (size class 112), could be 96 (size class 96), rearrange to struct{trailers []string; contentType string; contentLength string; date string; h Header; httpResCode int; streamID uint32; endStream bool} for optimal size (14.29% savings)go.googlesource.com/go/src/net/http/h2_bundle.go:9474:28: struct has size 56 (size class 64), could be 48 (size class 48), rearrange to struct{method string; url *url.URL; h Header; allocatePromisedID func() (uint32, error); streamID uint32; promisedID uint32} for optimal size (25.00% savings)go.googlesource.com/go/src/net/http/server.go:418:15: struct has size 224 (size class 224), could be 208 (size class 208), rearrange to struct{trailers []string; cw chunkWriter; reqBody io.ReadCloser; contentLength int64; closeNotifyCh chan bool; conn *conn; cancelCtx context.CancelFunc; status int; handlerHeader Header; req *Request; w *bufio.Writer; written int64; writeContinueMu sync.Mutex; canWriteContinue atomicBool; handlerDone atomicBool; didCloseNotify int32; dateBuf [29]byte; clenBuf [10]byte; statusBuf [3]byte; requestBodyLimitHit bool; wants10KeepAlive bool; wroteContinue bool; wantsClose bool; closeAfterReply bool; wroteHeader bool; calledHeader bool} for optimal size (7.14% savings)go.googlesource.com/go/src/net/http/transfer.go:58:21: struct has size 152 (size class 160), could be 128 (size class 128), rearrange to struct{TransferEncoding []string; Body io.Reader; BodyCloser io.Closer; bodyReadError error; Method string; ContentLength int64; Header Header; Trailer Header; ByteReadCh chan readResult; Close bool; IsResponse bool; ResponseToHEAD bool; FlushHeaders bool} for optimal size (20.00% savings)go.googlesource.com/go/src/runtime/mfixalloc.go:27:15: struct has size 72 (size class 80), could be 64 (size class 64), rearrange to struct{size uintptr; first func(arg unsafe.Pointer, p unsafe.Pointer); arg unsafe.Pointer; list *mlink; chunk uintptr; inuse uintptr; stat *sysMemStat; nchunk uint32; zero bool} for optimal size (20.00% savings)go.googlesource.com/go/src/runtime/mgc.go:1009:10: struct has size 400 (size class 416), could be 376 (size class 384), rearrange to struct{wbufSpans struct{lock mutex; free mSpanList; busy mSpanList}; assistQueue struct{lock mutex; q gQueue}; sweepWaiters struct{lock mutex; list gList}; heap2 uint64; heap1 uint64; bytesMarked uint64; heap0 uint64; pauseStart int64; pauseNS int64; tstart int64; tEnd int64; nFlushCacheRoots int; empty lfstack; nBSSRoots int; nSpanRoots int; nStackRoots int; tMarkTerm int64; tMark int64; bgMarkReady note; full lfstack; mode gcMode; tSweepTerm int64; totaltime int64; initialHeapLive uint64; nDataRoots int; heapGoal uint64; cycles uint32; stwprocs int32; maxprocs int32; _ uint32; markDoneSema uint32; startSema uint32; nwait uint32; nproc uint32; markrootJobs uint32; markrootNext uint32; bgMarkDone uint32; pad0 cpu.CacheLinePad; userForced bool} for optimal size (7.69% savings)go.googlesource.com/go/src/runtime/mgcscavenge.go:161:14: struct has size 40 (size class 48), could be 32 (size class 32), rearrange to struct{lock mutex; g *g; timer *timer; sysmonWake uint32; parked bool} for optimal size (33.33% savings)go.googlesource.com/go/src/runtime/proc.go:1977:17: struct has size 40 (size class 48), could be 32 (size class 32), rearrange to struct{lock mutex; newm muintptr; wake note; haveTemplateThread uint32; waiting bool} for optimal size (33.33% savings)go.googlesource.com/go/src/runtime/proc.go:5159:17: struct has size 32 (size class 32), could be 24 (size class 24), rearrange to struct{schedwhen int64; syscallwhen int64; schedtick uint32; syscalltick uint32} for optimal size (25.00% savings)go.googlesource.com/go/src/runtime/runtime2.go:753:10: struct has size 32 (size class 32), could be 24 (size class 24), rearrange to struct{runnable gQueue; n int32; user bool} for optimal size (25.00% savings)go.googlesource.com/go/src/runtime/trace.go:761:17: struct has size 40 (size class 48), could be 32 (size class 32), rearrange to struct{stk [0]uintptr; link traceStackPtr; hash uintptr; n int; id uint32} for optimal size (33.33% savings)go.googlesource.com/go/src/syscall/exec_darwin.go:11:18: struct has size 56 (size class 64), could be 48 (size class 48), rearrange to struct{Chroot string; Credential *Credential; Pgid int; Ctty int; Ptrace bool; Setpgid bool; Setctty bool; Noctty bool; Foreground bool; Setsid bool} for optimal size (25.00% savings)go.googlesource.com/go/src/testing/testing.go:386:13: struct has size 360 (size class 384), could be 352 (size class 352), rearrange to struct{sub []*T; output []byte; start time.Time; cleanupPc []uintptr; cleanups []func(); creator []uintptr; name string; runner string; tempDir string; cleanupName string; tempDirErr error; w io.Writer; duration time.Duration; barrier chan bool; chatty *chattyPrinter; raceErrors int; helpers map[string]struct{}; parent *common; level int; signal chan bool; mu sync.RWMutex; tempDirMu sync.Mutex; hasSub int32; tempDirSeq int32; finished bool; done bool; skipped bool; failed bool; ran bool; bench bool} for optimal size (8.33% savings)

Kubernetes:

github.com/kubernetes/kubernetes/cmd/kubeadm/app/cmd/init.go:113:15: struct has size 152 (size class 160), could be 136 (size class 144), rearrange to struct{certificatesDir string; outputWriter io.Writer; client kubernetes.Interface; kubeconfigDir string; kubeconfigPath string; dryRunDir string; patchesDir string; ignorePreflightErrors sets.String; cfg *kubeadm.InitConfiguration; externalCA bool; dryRun bool; skipTokenPrint bool; uploadCerts bool; skipCertificateKeyPrint bool} for optimal size (10.00% savings)github.com/kubernetes/kubernetes/cmd/kubeadm/app/cmd/phases/workflow/phase.go:26:12: struct has size 184 (size class 192), could be 176 (size class 176), rearrange to struct{Phases []Phase; Aliases []string; InheritFlags []string; Short string; Long string; Example string; Name string; Run func(data interface{}) error; RunIf func(data interface{}) (bool, error); LocalFlags *pflag.FlagSet; ArgsValidator cobra.PositionalArgs; RunAllSiblings bool; Hidden bool} for optimal size (8.33% savings)

gRPC-go

As of commit 5d7f8c9, these are the reports

google.golang.org/grpc/server.go:122:20: struct has size 296 (size class 320), could be 288 (size class 288), rearrange to struct{keepaliveParams keepalive.ServerParameters; chainUnaryInts []UnaryServerInterceptor; chainStreamInts []StreamServerInterceptor; cp Compressor; dc Decompressor; keepalivePolicy keepalive.EnforcementPolicy; statsHandler stats.Handler; codec baseCodec; creds credentials.TransportCredentials; maxSendMessageSize int; headerTableSize *uint32; maxReceiveMessageSize int; inTapHandle tap.ServerInHandle; unknownStreamDesc *StreamDesc; streamInt StreamServerInterceptor; unaryInt UnaryServerInterceptor; maxHeaderListSize *uint32; connectionTimeout time.Duration; writeBufferSize int; readBufferSize int; initialConnWindowSize int32; initialWindowSize int32; maxConcurrentStreams uint32; numServerWorkers uint32} for optimal size (10.00% savings)google.golang.org/grpc/stream.go:390:19: struct has size 272 (size class 288), could be 248 (size class 256), rearrange to struct{opts []CallOption; beginTime time.Time; buffer []func(a *csAttempt) error; comp encoding.Compressor; ctx context.Context; codec baseCodec; cp Compressor; numRetries int; cancel context.CancelFunc; desc *StreamDesc; cc *ClientConn; methodConfig *MethodConfig; callInfo *callInfo; retryThrottler *retryThrottler; binlog *binarylog.MethodLogger; attempt *csAttempt; numRetriesSincePushback int; callHdr *transport.CallHdr; bufferSize int; mu sync.Mutex; finished bool; serverHeaderBinlogged bool; committed bool; sentLast bool; firstAttempt bool} for optimal size (11.11% savings)google.golang.org/grpc/stream.go:1119:21: struct has size 224 (size class 224), could be 208 (size class 208), rearrange to struct{opts []CallOption; codec baseCodec; dc Decompressor; decomp encoding.Compressor; comp encoding.Compressor; cp Compressor; t transport.ClientTransport; ctx context.Context; s *transport.Stream; desc *StreamDesc; p *parser; callInfo *callInfo; cancel context.CancelFunc; callHdr *transport.CallHdr; ac *addrConn; mu sync.Mutex; decompSet bool; sentLast bool; finished bool} for optimal size (7.14% savings)google.golang.org/grpc/stats/stats.go:41:12: struct has size 40 (size class 48), could be 32 (size class 32), rearrange to struct{BeginTime time.Time; Client bool; FailFast bool} for optimal size (33.33% savings)

dgraph/dgraph

As of commit 74224f5, we’ve got reports

github.com/dgraph-io/dgraph/worker/task.go:1637:22: struct has size 184 (size class 192), could be 176 (size class 176), rearrange to struct{ineqValueToken []string; uidsPresent []uint64; threshold []int64; eqTokens []types.Val; tokens []string; fname string; n int; geoQuery *types.GeoQueryData; fnType FuncType; regex *regexp.Regexp; atype types.TypeID; intersectDest bool; isFuncAtRoot bool; isStringFn bool} for optimal size (8.33% savings)github.com/dgraph-io/dgraph/x/config.go:59:20: struct has size 232 (size class 240), could be 216 (size class 224), rearrange to struct{ZeroAddr []string; StartTime time.Time; WhiteListedIPRanges []IPRange; EncryptionKey SensitiveByteSlice; ExportPath string; MyAddr string; TLSServerConfig *tls.Config; RaftId uint64; Tracing float64; MaxRetries int; TLSClientConfig *tls.Config; AbortOlderThan time.Duration; SnapshotAfter int; NumPendingProposals int; LudicrousConcurrency int; LogRequest int32; ProposedGroupId uint32; StrictMutations bool; LudicrousMode bool; AclEnabled bool; HardSync bool} for optimal size (6.67% savings)github.com/dgraph-io/dgraph/x/keys.go:195:16: struct has size 72 (size class 80), could be 56 (size class 64), rearrange to struct{Attr string; Term string; Uid uint64; StartUid uint64; Count uint32; HasStartUid bool; ByteType byte; bytePrefix byte} for optimal size (20.00% savings)

By using structslop, you can recoup memory instead of allocating larger heaps or at least wasting memory, this computer program will help you save a whole bunch, basically your personalized GEICO Gecko #15MinutesCouldSaveYouALot!

Like the GEICO Gecko, we’ll help you with those RAM and efficiency savings!

Conclusion

We are making structslop available for every programmer! You can go get it at https://github.com/orijtech/structslop

go get gitHub.com/orijtech/structslop/cmd/structslop

Please enjoy, share with your friends, provide feedback, file issues, report back!

At Orijtech, Inc., we build tools, contribute to the Go project in all aspects and build observability and high performance tooling!

Thank you.

From: Cuong Manh Le, Emmanuel T Odeke

Engineering Department @ Orijtech, Inc.

Image for post
Image for post

Orijtech Developers

developer tools

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store