diff --git a/docs/20-slice.md b/docs/20-slice.md index 2421e40..560f23d 100644 --- a/docs/20-slice.md +++ b/docs/20-slice.md @@ -5,6 +5,8 @@ comments: true # Not understanding slice length and capacity +![](img/20-slice.png) + It’s pretty common for Go developers to mix slice length and capacity or not understand them thoroughly. Assimilating these two concepts is essential for efficiently handling core operations such as slice initialization and adding elements with append, copying, or slicing. This misunderstanding can lead to using slices suboptimally or even to memory leaks. In Go, a slice is backed by an array. That means the slice’s data is stored contiguously in an array data structure. A slice also handles the logic of adding an element if the backing array is full or shrinking the backing array if it’s almost empty. diff --git a/docs/28-maps-memory-leaks.md b/docs/28-maps-memory-leaks.md index 6b72def..2248acd 100644 --- a/docs/28-maps-memory-leaks.md +++ b/docs/28-maps-memory-leaks.md @@ -5,6 +5,8 @@ comments: true # Maps and memory leaks +![](img/28-maps-memory-leaks.png) + When working with maps in Go, we need to understand some important characteristics of how a map grows and shrinks. Let’s delve into this to prevent issues that can cause memory leaks. First, to view a concrete example of this problem, let’s design a scenario where we will work with the following map: diff --git a/docs/56-concurrency-faster.md b/docs/56-concurrency-faster.md index cd63834..8a2328d 100644 --- a/docs/56-concurrency-faster.md +++ b/docs/56-concurrency-faster.md @@ -5,6 +5,8 @@ comments: true # Thinking concurrency is always faster +![](img/56-concurrency-faster.png) + A misconception among many developers is believing that a concurrent solution is always faster than a sequential one. This couldn’t be more wrong. The overall performance of a solution depends on many factors, such as the efficiency of our code structure (concurrency), which parts can be tackled in parallel, and the level of contention among the computation units. This post reminds us about some fundamental knowledge of concurrency in Go; then we will see a concrete example where a concurrent solution isn’t necessarily faster. ## Go Scheduling diff --git a/docs/89-benchmarks.md b/docs/89-benchmarks.md index e4f024a..dce0d76 100644 --- a/docs/89-benchmarks.md +++ b/docs/89-benchmarks.md @@ -5,6 +5,8 @@ comments: true # Writing inaccurate benchmarks +![](img/89-benchmarks.png) + In general, we should never guess about performance. When writing optimizations, so many factors may come into play that even if we have a strong opinion about the results, it’s rarely a bad idea to test them. However, writing benchmarks isn’t straightforward. It can be pretty simple to write inaccurate benchmarks and make wrong assumptions based on them. The goal of this post is to examine four common and concrete traps leading to inaccuracy: * Not resetting or pausing the timer diff --git a/docs/98-profiling-execution-tracing.md b/docs/98-profiling-execution-tracing.md index 88561bf..f0d3f4a 100644 --- a/docs/98-profiling-execution-tracing.md +++ b/docs/98-profiling-execution-tracing.md @@ -5,6 +5,8 @@ comments: true # Not using Go diagnostics tooling +![](img/98-profiling-execution-tracing.png) + Go offers a few excellent diagnostics tools to help us get insights into how an application performs. This post focuses on the most important ones: profiling and the execution tracer. Both tools are so important that they should be part of the core toolset of any Go developer who is interested in optimization. First, let’s discuss profiling. ## Profiling diff --git a/docs/img/20-slice.png b/docs/img/20-slice.png new file mode 100644 index 0000000..a119936 Binary files /dev/null and b/docs/img/20-slice.png differ diff --git a/docs/img/28-maps-memory-leaks.png b/docs/img/28-maps-memory-leaks.png new file mode 100644 index 0000000..88d702a Binary files /dev/null and b/docs/img/28-maps-memory-leaks.png differ diff --git a/docs/img/56-concurrency-faster.png b/docs/img/56-concurrency-faster.png new file mode 100644 index 0000000..a3b8c27 Binary files /dev/null and b/docs/img/56-concurrency-faster.png differ diff --git a/docs/img/89-benchmarks.png b/docs/img/89-benchmarks.png new file mode 100644 index 0000000..3415bc1 Binary files /dev/null and b/docs/img/89-benchmarks.png differ diff --git a/docs/img/98-profiling-execution-tracing.png b/docs/img/98-profiling-execution-tracing.png new file mode 100644 index 0000000..bb39fa6 Binary files /dev/null and b/docs/img/98-profiling-execution-tracing.png differ diff --git a/site/20-slice/index.html b/site/20-slice/index.html index f102a76..71fd6ee 100644 --- a/site/20-slice/index.html +++ b/site/20-slice/index.html @@ -847,6 +847,7 @@

Not understanding slice length and capacity

+

It’s pretty common for Go developers to mix slice length and capacity or not understand them thoroughly. Assimilating these two concepts is essential for efficiently handling core operations such as slice initialization and adding elements with append, copying, or slicing. This misunderstanding can lead to using slices suboptimally or even to memory leaks.

In Go, a slice is backed by an array. That means the slice’s data is stored contiguously in an array data structure. A slice also handles the logic of adding an element if the backing array is full or shrinking the backing array if it’s almost empty.

Internally, a slice holds a pointer to the backing array plus a length and a capacity. The length is the number of elements the slice contains, whereas the capacity is the number of elements in the backing array, counting from the first element in the slice. Let’s go through a few examples to make things clearer. First, let’s initialize a slice with a given length and capacity:

diff --git a/site/28-maps-memory-leaks/index.html b/site/28-maps-memory-leaks/index.html index 7f25482..0b32c8a 100644 --- a/site/28-maps-memory-leaks/index.html +++ b/site/28-maps-memory-leaks/index.html @@ -847,6 +847,7 @@

Maps and memory leaks

+

When working with maps in Go, we need to understand some important characteristics of how a map grows and shrinks. Let’s delve into this to prevent issues that can cause memory leaks.

First, to view a concrete example of this problem, let’s design a scenario where we will work with the following map:

m := make(map[int][128]byte)
diff --git a/site/56-concurrency-faster/index.html b/site/56-concurrency-faster/index.html
index 4dbf201..a8c3e26 100644
--- a/site/56-concurrency-faster/index.html
+++ b/site/56-concurrency-faster/index.html
@@ -927,6 +927,7 @@
 
 
 

Thinking concurrency is always faster

+

A misconception among many developers is believing that a concurrent solution is always faster than a sequential one. This couldn’t be more wrong. The overall performance of a solution depends on many factors, such as the efficiency of our code structure (concurrency), which parts can be tackled in parallel, and the level of contention among the computation units. This post reminds us about some fundamental knowledge of concurrency in Go; then we will see a concrete example where a concurrent solution isn’t necessarily faster.

Go Scheduling

A thread is the smallest unit of processing that an OS can perform. If a process wants to execute multiple actions simultaneously, it spins up multiple threads. These threads can be:

diff --git a/site/89-benchmarks/index.html b/site/89-benchmarks/index.html index 0732577..9cffda5 100644 --- a/site/89-benchmarks/index.html +++ b/site/89-benchmarks/index.html @@ -955,6 +955,7 @@

Writing inaccurate benchmarks

+

In general, we should never guess about performance. When writing optimizations, so many factors may come into play that even if we have a strong opinion about the results, it’s rarely a bad idea to test them. However, writing benchmarks isn’t straightforward. It can be pretty simple to write inaccurate benchmarks and make wrong assumptions based on them. The goal of this post is to examine four common and concrete traps leading to inaccuracy:

  • Not resetting or pausing the timer
  • diff --git a/site/98-profiling-execution-tracing/index.html b/site/98-profiling-execution-tracing/index.html index ab20fa2..5c71cfb 100644 --- a/site/98-profiling-execution-tracing/index.html +++ b/site/98-profiling-execution-tracing/index.html @@ -1009,6 +1009,7 @@

    Not using Go diagnostics tooling

    +

    Go offers a few excellent diagnostics tools to help us get insights into how an application performs. This post focuses on the most important ones: profiling and the execution tracer. Both tools are so important that they should be part of the core toolset of any Go developer who is interested in optimization. First, let’s discuss profiling.

    Profiling

    Profiling provides insights into the execution of an application. It allows us to resolve performance issues, detect contention, locate memory leaks, and more. These insights can be collected via several profiles:

    diff --git a/site/img/20-slice.png b/site/img/20-slice.png new file mode 100644 index 0000000..a119936 Binary files /dev/null and b/site/img/20-slice.png differ diff --git a/site/img/28-maps-memory-leaks.png b/site/img/28-maps-memory-leaks.png new file mode 100644 index 0000000..88d702a Binary files /dev/null and b/site/img/28-maps-memory-leaks.png differ diff --git a/site/img/56-concurrency-faster.png b/site/img/56-concurrency-faster.png new file mode 100644 index 0000000..a3b8c27 Binary files /dev/null and b/site/img/56-concurrency-faster.png differ diff --git a/site/img/89-benchmarks.png b/site/img/89-benchmarks.png new file mode 100644 index 0000000..3415bc1 Binary files /dev/null and b/site/img/89-benchmarks.png differ diff --git a/site/img/98-profiling-execution-tracing.png b/site/img/98-profiling-execution-tracing.png new file mode 100644 index 0000000..bb39fa6 Binary files /dev/null and b/site/img/98-profiling-execution-tracing.png differ diff --git a/site/sitemap.xml.gz b/site/sitemap.xml.gz index 573dcf4..849b9d8 100644 Binary files a/site/sitemap.xml.gz and b/site/sitemap.xml.gz differ