29/02/2024 6 Minutes read

How our Objects can lead to a better digital sobriety awareness ?

tl;dr : mind about your objects memory layout is care about your digital footprint.

As an iOS mobile software engineer, we face daily challenges in choosing effective app architecture, aligning with client requirements, budget constraints, and staffing. Recently, environmental impact has become a new concern affecting our work.

For many of us, we’ve already recognized various limitations during mobile software development process, including connectivity issues (tips: disable Roaming Data while traveling… ), battery constraints (buy external battery bank) and storage limitations (expend our Cloud resources).

After mentioning those significant points above, how could we identify and quantify our energy consumption in order to reduce our carbon footprint? During WWDC, Apple teams demonstrate their choice of materials used in production, as well as their processes for producing new products with recycled parts.

As programmers, what do we manipulate the most in our daily lives? Variables, objects and data structures. Here, we are able to quantify this information, determine the size of payloads that are transferred (remember the last hefty phone bills due to data consumption) and evaluate how much space this information also occupies when stored.

To illustrate my purpose, let’s get back to the basics with the definition of Types and MemoryLayout.

Even in modern programming in Swift language specification it defines only “two” kinds of Types (named and compound types).
To ensure we are discussing the same information, I will just put some grammar that should make sense even for non-iOS developper.

Let’s got step by step together, first we are going to talk about types then the memory layout of value types in Swift to conclude wrapping up on how it is possible to reduce and evaluate our carbon footprint.

TYPES

– First, let’s discuss the types, which I will refer to as primitive types. These will technically include: Bool, Int, Float, Double, and so on.

let isValid: Bool = false
let age: Int = 3
var positionX: Double = 2.2

Then we have complex objects which can be categorized into two groups. This article won’t go into further detail about reference and value types (documentation available here) but they are very interesting to explore, especially regarding pointers.

  • Value types: struct, enum, tuple
// struct (named type)
struct Animal {
var legs: Int
var isFluffy: Bool
}
// enum (named type)
enum Week { case mon, tue, wed, thu, fri, sat, sun }
// tupple (compound type)
let coordinate = (0.0, 0.0)
let coordX = coordinate.0
let coordY = coordinate.1
  • References Type: class
class Animal {
var legs: Int
var isFluffy: Bool

init(legs: Int, isFluffy: Bool) {
self.legs = legs
self.isFluffy = isFluffy
}
}
  • Data structs, composed of objects, are designed to manipulate them more effectively, particularly when referring to algorithm optimization with (Big O notation).
    One way to categorize them is into Linear and Non-linear families :
    Array / List / Queue / Stack
    Hash table (Map) / Tree / Trie / Graph

Memory layout of value types

When we talk about how computers store information, it’s all about bits and bytes. Think of a bit as the smallest unit of information, represented by either a 0 or a 1. These bits get grouped together into sets of 8 (bits) to form what’s called a byte.

Now, let’s take a journey through the evolution of computers, using video game consoles as our guide. You might remember classics like the NES or the Super NES. These consoles started with simple 8-bit technology, which means they could process information in small, 8-bit chunks. As technology advanced, consoles like the PlayStation jumped to 32 bits, and then even higher with consoles like the Nintendo N64 (64bits).

But it’s not just about gaming consoles. The computers we use also evolved over time. We went from older systems like x32_ABI to newer, more powerful ones like x86–64 and ARM.

So, what does this mean for how computers work? Imagine the computer’s brain, called the CPU, as a reader going through these chunks of information in cycles. Each type of computer architecture has its own way of organizing these chunks, and the CPU knows how to read them based on the architecture it’s designed for.

In simpler terms, the CPU reads these groups of bytes by cycle like following a recipe (let’s cook).

illustration of array of bytes for different Integers representation (Int 16, Int 32, Int 64)

MemoryLayout is the source of information on how an allocated object is bound in memory. It defines the three following keys properties : alignment, size, stride. You can find more details in the documentation.

Let’s dive deep into a concrete daily use case with those following definition of Puppy & Kitty objects :

struct Puppy {  
let age: Int
let isPureBreed: Bool
}
struct Kitty {
let isPureBreed: Bool
let age: Int
}

Then let’s inspect each of them with MemoryLayout for and see what information they provide.

Print(“Puppy”)
print(MemoryLayout<Puppy>.size) // 9
print(MemoryLayout<Puppy>.stride) // 16
print(MemoryLayout<Puppy>.alignment) // 8
print("Kitty")
print(MemoryLayout<Kitty>.size) // 16
print(MemoryLayout<Kitty>.stride) // 16
print(MemoryLayout<Kitty>.alignment) // 8
// Int & Bool: MemoryLayout for 
print(MemoryLayout<Int>.stride) // 8
print(MemoryLayout<Bool>.stride) // 1

How can we understand the differences between our Puppy and our Kitten? Both are using Int and Bool properties, but their stride is the same while their size differs ?

Eventually the size property could lead us to believe that an array of Puppies [Puppy] would occupy less space in memory since its size value is lower than the [Kitty] ? However, this is not how they are actually stored.

Expectation of MemoryLayout representation according to .size property

According to the architecture and cycles, our objects are stored as illustrated below. We should refers to the MemoryLayout with .stride property.

Actual MemoryLayout representation according to .stride property, of the arrays of [Puppy] & [Kitty]

For the case of the first Puppy of the array, we can notice here that there is some empty space annotated with ‘-’ between the Bool property and the next object instance in the array. (empty space occupying Memory bits index 10 to 16).
While for the first instance of Kitty, we can observe that the empty space ‘-’ in Memory bits occupies from index 2 to 8, just before the first property ‘age’ of type ‘Int’, before the second instance of Kitty.

Let’s continue with one more step ahead

Since those animals are lovable, our users required us to update our struct adding a new boolean property isCute: Bool. According to your intuition, how would you implement the struct ?

// First choices of implementation
struct Puppy {
let age: Int
let isTrained: Bool
let isCute: Bool
}

// Or

struct Puppy {
let isTrained: Bool
let isCute: Bool
let age: Int
}

The optimum way in our use case, would be to make those 2 booleans properties follow each other in MemoryLayout (according to our cycle).
The fact in MemoryLayout they will still occupy 16 bits on .stride . (and a total of 6 empty bits)

Puppy
10 (size)
16 (stride)
8 (alignement)
optimised MemoryLayout representation of our new struct with isCute parameter (with less empty bits)
// Second choice choice of implementation
struct Kitty {
let isTrained: Bool
let age: Int
let isCute: Bool
}

As a result we will have a non optimal MemoryLayout for our properties, as illustrated bellow for better understanding. (with a total of 14 empty bits)

Kitty
17 (size)
24 (stride)
8 (alignement)
non-optimised MemoryLayout representation of our struct (with extra empty bits)

To wrap it up

We have been witness through those illustrative use cases, that we are able to quantify the optimisation of a simple Object (just by considering the arrangement of its properties).

Few words about Digital Sobriety awareness as an engineers. This article deals with a simple struct of cute animal. But lets overlook the broader applying these memory consumption extending to Data Structs, that implies the manipulation of thousand or few hundred thousand of animals (Objects). Or scaling up to a Data Center applying our object for Machine Learning datasource case.

A mere rearrangement of lines of code hold the power of significantly reduce our carbon footprint. Whether it’s optimizing memory usage, refining algorithms, streamlining network data transfers, or even enhancing battery efficiency, every thoughtful refinement contributes to a more sustainable digital ecosystem.

happy coding


How our Objects can lead to a better digital sobriety awareness ? was originally published in ekino-france on Medium, where people are continuing the conversation by highlighting and responding to this story.