Saving Memory In Golang By Composing Struct Correctly

Ananda Dwi Rahma
6 min readJan 19, 2022

--

(If you compose data type of struct carelessly, it will waste your memory)

Photo by Jorge Salvador on Unsplash

Definition of Struct

A structure or struct in Golang is a user-defined type that allows to group/combine items of possibly different types into a single type. Any real-world entity which has some set of properties/fields can be represented as a struct. This concept is generally compared with the classes in object-oriented programming. It can be termed as a lightweight class that does not support inheritance but supports composition. (source: https://www.geeksforgeeks.org/structures-in-golang/)

In my simple definition, struct is a collection of variables or properties which are wrapped as a new data type with a certain name. It has name, attribute, and data type.

For instance:

type Employee struct {
Name string
Age int64
}

“Employee” is the name of struct, “Name and Age is the attribute, “string and int64” is the data type.

However, in this article I won’t talk about how we use that, but I will talk about what happens when we call a struct.

What do you know?

If you compose data type of struct carelessly, it will waste your memory.

Let’s try!

Let’s create a simple struct with five attributes and some kinds of data type:

type Employee struct {
IsActive bool
Age int64
IsMarried bool
Name string
Photo float32
}
Element of Memory Size
Elements Of Memory Size.

You can use unsafe syntax to check the element of memory size. For instance: https://go.dev/play/p/msL1l2TU-lZ.

Let’s count the memory size!

Struct Employee: IsActive(boolean) 1 byte + Age(int64) 8 byte+ IsMarried(boolean) 1 byte + Name(string) 16 byte + Photo(float32) 4 byte = 30 byte

What is the result of 30 byte? Let’s try!

Surprisingly, the result is 48 bytes. What has occurred with the struct? Let’s check!

Types Of Processor

We know that CPU 32-bit and CPU 64-bit are the most popular types of CPU. Nevertheless, how does it work?

Imagine that we have a CPU 64-bit. It means, the CPU has transfer capability 64-bit data per clock cycle. In my simple version, clock cycle is how much time needed for CPU to process an information.

Are you confused? Don’t worry, let’s look the analogy!

Imagine there are two people who will unload many boxes for moving to the office. We can say as follows:

  • Trolley = type of CPU architecture (64-bit or 32-bit)
  • Box = data
  • How much time needed for those people to move the boxes = cycle clock

Same as CPU, those people will move the boxes to the office according to the capability of the trolley. The bigger the trolley, more boxes are carried, and the faster it will take. Thus, we can say CPU 32-bit has capability to transform 4 byte data in 1 cycle and CPU 64-bit has capability to transform 8 byte data in 1 cycle (32 bit= 4 byte, 64 bit = 8 byte). For the further information, we will explain about memory size in byte.

Study Case

We will still use Employee struct. It has some attributes and data types. Let’s try to calculate the memory size.

The table above is the size for each attribute of Employee struct. After we know about the size, let’s try to calculate how much time needed for CPU to process the information(it’s called Cycle Clock).

Imagine we have CPU 64-bit. It means that CPU can transform 8 byte data in each cycle. Based on the analogy above, there are eight boxes in each cycle. It is the representation from capability of CPU which is 8 byte.

Cycle 1

The first attribute is IsActive. The data type is bool. The size of bool is 1 byte. Thus, currently cycle 1 has filled by attribute IsActive(bool~1 byte). The next attribute is Age. The data type is int64. The size of int64 is 8 byte. Because the remaining memory size in cycle 1 is only 7 bytes, Age attribute can’t be entered in cycle 1. Therefore, it will be entered in cycle 2. However, “what about the remaining size?”. It will be wasted 7 bytes.

The interesting point is “If there is no space enough for next attribute in a cycle, it will be entered in next cycle.”

Cycle 2

In cycle 2 is filled by Age attribute, the data type is int64 and the size is 8 bytes.

Cycle 3

The third attribute is IsMarried. The data type is bool and the size is 1 byte. Currently in cycle 3 is filled by attribute IsMarried(bool~1 byte) and the remaining memory size is 7 bytes. Then, the next attribute is Name with type string and size 16 bytes. Same as the case in cycle 1, because there is no enough space for the next attribute, it will be entered in the next cycle. It means that there are 7 bytes memory wasted.

Cycle 4 & Cycle 5

The fourth attribute is Name. The data type is string and the size is 16 bytes. As a result, it will be entered in two cycles, 8 bytes in cycle 4 and 8 bytes in cycle 5.

Cycle 6

The last attribute is Photo. The data type is float32 and the size is 4 bytes. As a result, in cycle 6 is filled by attribute Photo(float32 ~4 bytes), and it will waste the remaining 4 bytes.

The conclusion:

  • Total clock cycle = 6 clock cycles.
  • Size of struct = 48 bytes.
  • Total memory wasted = 18 bytes.

Amazing! Only composing data type carelessly makes the size of struct bigger.

So, how to solve it?

Actually we must compose the sequence of data type according to their size. However, the easiest way is:

Put the field in descending order of element’s memory size.

Let’s try!

Based on the tips, we must put the field in descending order of element’s memory size. As a result, the sequence attributes of struct is:

type Employee struct {
Name string
Age int64
Photo float32
IsActive bool
IsMarried bool
}

Then, let’s count the cycle clock.

Cycle 1 & Cycle 2

The first attribute is Name. The data type is string and the size is 16 bytes. Thus, it will be entered in two cycles, 8 bytes in cycle 1 and 8 bytes in cycle 2.

Cycle 3

In cycle 3 is filled by Age attribute, the data type is int64 and the size is 8 bytes.

Cycle 4

The next attribute is Photo. The data type is float32 and the size is 4 bytes. Thus, currently cycle 4 is filled by attribute Photo(float32~4 bytes) and the remaining size is 4 bytes. The next attribute are IsActive(bool~1 byte) and IsMarried(bool~1 byte). Because the total size of last two attributes is only 2 bytes, we can put it in cycle 4. As a result, the cycle 4 is filled by Photo(float32~4 bytes), IsActive(bool~1 byte), IsMarried(bool~1 byte), and only 2 bytes memory wasted.

The conclusion:

  • Total clock cycle = 4 clock cycles.
  • Size of struct = 32 bytes.
  • Total memory wasted = 2 bytes.

Do you believe it? Let’s try!

Viola!

What is the conclusion?

  • You should be careful to define the data type of field in struct.
  • The easiest way to define struct is “put field in descending order of element’s memory size
  • However, from my point of view: don’t be overthinking, because it will waste your time to code. The second one, if you feel the struct unreadable after you sort, ignore the tips. I think the readability of the code is more important.

--

--