<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kostiantyn Lysenko</title>
    <description>The latest articles on DEV Community by Kostiantyn Lysenko (@kostiantyn_lysenko_5a13a9).</description>
    <link>https://dev.to/kostiantyn_lysenko_5a13a9</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1669154%2Fe7ce40e1-53ae-4a83-beef-336648c6caa9.png</url>
      <title>DEV Community: Kostiantyn Lysenko</title>
      <link>https://dev.to/kostiantyn_lysenko_5a13a9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kostiantyn_lysenko_5a13a9"/>
    <language>en</language>
    <item>
      <title>Algorithm Complexity with Go — Linear Time Complexity O(n)</title>
      <dc:creator>Kostiantyn Lysenko</dc:creator>
      <pubDate>Sun, 14 Jul 2024 09:13:10 +0000</pubDate>
      <link>https://dev.to/kostiantyn_lysenko_5a13a9/algorithm-complexity-with-go-linear-time-complexity-on-132i</link>
      <guid>https://dev.to/kostiantyn_lysenko_5a13a9/algorithm-complexity-with-go-linear-time-complexity-on-132i</guid>
      <description>&lt;h3&gt;
  
  
  Algorithm Complexity with Go — Linear Time Complexity O(n)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVjGV5syFHANeu3RVtAdDxA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVjGV5syFHANeu3RVtAdDxA.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today, we will focus on linear time complexity, often denoted as &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linear time complexity&lt;/strong&gt; implies that the time required to complete the algorithm grows directly proportional to the input size. This type of complexity is efficient and clean because it scales predictably with input size. It is significant because it strikes a balance between simplicity and performance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Analogy to Understand Linear Time Complexity
&lt;/h4&gt;

&lt;p&gt;Imagine you are a &lt;strong&gt;postman&lt;/strong&gt; 👮🏻‍♂️ delivering letters. You have a list of addresses, and you need to deliver one letter to each address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you have 10 letters to deliver, it takes 10 stops.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;If you have 100 letters to deliver, it takes 100 stops.&lt;br&gt;&lt;br&gt;
If you have 1 000 000 letters to deliver, it takes 1 000 000 stops.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each stop takes a consistent amount of time to park, deliver the letter, and return to the van.&lt;/p&gt;

&lt;p&gt;The time taken grows proportionally with the number of letters.&lt;/p&gt;

&lt;p&gt;As each stop takes the same amount of time as each memory access and write operation &lt;strong&gt;takes a consistent amount of time&lt;/strong&gt; , so doubling the number of items roughly doubles the total time needed.&lt;/p&gt;
&lt;h4&gt;
  
  
  Real-world examples
&lt;/h4&gt;

&lt;p&gt;Let’s consider the most common real-world examples of operations that typically have linear time complexity:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iterating through a list&lt;/strong&gt; to perform some action on each element. For example, printing each element of a list of names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
    slice := []string{"Alice", "Bob", "Charlie"}
    for _, name := range slice {
        fmt.Println(name)
    }
    // Output: Alice
    // Output: Bob
    // Output: Charlie
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Simple search&lt;/strong&gt; operations in an unsorted list. Finding a specific number in an unsorted list of integers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
    slice := []int{30, 10, 2, 15, 130}
    target := 30

    for _, value := range slice {
       if value == target {
          fmt.Println(value)
       }
    }

    // Output: 30
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Summing&lt;/strong&gt; all elements in a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5}

    sum := 0
    for _, value := range slice {
       sum += value
    }

    fmt.Println(sum) // Output: 15
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Copying&lt;/strong&gt; all elements from one list to another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5}

    newSlice := make([]int, len(slice))
    copy(newSlice, slice)

    fmt.Println(newSlice) // Output: [1, 2, 3, 4, 5]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Merging&lt;/strong&gt; two lists into one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3}
    slice2 := []int{4, 5, 6}

    mergedSlice := append(slice1, slice2...)

    fmt.Println(mergedSlice) // Output: [1, 2, 3, 4, 5, 6]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reversing&lt;/strong&gt; a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5}

    for i, j := 0, len(slice)-1; i &amp;lt; j; i, j = i+1, j-1 {
       slice[i], slice[j] = slice[j], slice[i]
    }

    fmt.Println(slice) // Output: [5, 4, 3, 2, 1]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Benchmarks
&lt;/h4&gt;

&lt;p&gt;Let’s benchmark &lt;strong&gt;copy()&lt;/strong&gt; and confirm its linear time complexity.&lt;br&gt;&lt;br&gt;
Create a file named &lt;em&gt;copy_benchmark_test.go&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "fmt"
    "testing"
)

// copyArray copies all elements from one slice to another
func copyArray(arr []int) []int {
    newArr := make([]int, len(arr))
    copy(newArr, arr)
    return newArr
}

// BenchmarkCopyArray benchmarks the copyArray function
func BenchmarkCopyArray(b *testing.B) {
    sizes := []int{1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000}

    for _, size := range sizes {
       b.Run("Size="+fmt.Sprint(size), func(b *testing.B) {
          arr := make([]int, size)
          for i := 0; i &amp;lt; size; i++ {
             arr[i] = i
          }

          b.ResetTimer()

          for i := 0; i &amp;lt; b.N; i++ {
             _ = copyArray(arr)
          }
       })
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go test -bench .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;goos: darwin
goarch: amd64
pkg: cache
cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
BenchmarkCopyArray
BenchmarkCopyArray/Size=1000
BenchmarkCopyArray/Size=1000-8 586563 1773 ns/op
BenchmarkCopyArray/Size=2000
BenchmarkCopyArray/Size=2000-8 459171 2582 ns/op
BenchmarkCopyArray/Size=3000
BenchmarkCopyArray/Size=3000-8 331647 3588 ns/op
BenchmarkCopyArray/Size=4000
BenchmarkCopyArray/Size=4000-8 263466 4721 ns/op
BenchmarkCopyArray/Size=5000
BenchmarkCopyArray/Size=5000-8 212155 5728 ns/op
BenchmarkCopyArray/Size=6000
BenchmarkCopyArray/Size=6000-8 179078 6902 ns/op
BenchmarkCopyArray/Size=7000
BenchmarkCopyArray/Size=7000-8 152635 7573 ns/op
BenchmarkCopyArray/Size=8000
BenchmarkCopyArray/Size=8000-8 142131 8423 ns/op
BenchmarkCopyArray/Size=9000
BenchmarkCopyArray/Size=9000-8 118581 9780 ns/op
BenchmarkCopyArray/Size=10000
BenchmarkCopyArray/Size=10000-8 109848 14530 ns/op
PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following chart shows the almost straight line ↗ that proves the &lt;strong&gt;copy()&lt;/strong&gt; operation has linear time complexity, O(n), as the time taken &lt;strong&gt;increases proportionally&lt;/strong&gt; with the size of the array:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A3zYfz2vW1yxeWmQFFon57g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A3zYfz2vW1yxeWmQFFon57g.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For very large slices, the linear time complexity means the performance will &lt;strong&gt;degrade in direct proportion to the size of the slice&lt;/strong&gt; , making it important to optimize such operations or consider parallel processing for performance improvement.&lt;/p&gt;

&lt;h4&gt;
  
  
  Summary
&lt;/h4&gt;

&lt;p&gt;Knowing about linear time complexity empowers to build efficient, scalable, and maintainable software, ensuring that applications perform well across a wide range of input sizes. This foundational knowledge is essential for anyone working with algorithms and data structures.&lt;/p&gt;

&lt;p&gt;Use linear time complexity solutions carefully only if you make sure it’s the best option and there is no way to use a linked list, map, etc. 🐈‍⬛&lt;/p&gt;

</description>
      <category>go</category>
      <category>softwareengineering</category>
      <category>algorithms</category>
      <category>programming</category>
    </item>
    <item>
      <title>Deadlocks in Go: Understanding and Preventing for Production Stability</title>
      <dc:creator>Kostiantyn Lysenko</dc:creator>
      <pubDate>Sun, 23 Jun 2024 00:21:30 +0000</pubDate>
      <link>https://dev.to/kostiantyn_lysenko_5a13a9/deadlocks-in-go-understanding-and-preventing-for-production-stability-35go</link>
      <guid>https://dev.to/kostiantyn_lysenko_5a13a9/deadlocks-in-go-understanding-and-preventing-for-production-stability-35go</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6ydlpnT4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/512/1%2AUKEybbNsF5e-8WEP-zTbDg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6ydlpnT4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/512/1%2AUKEybbNsF5e-8WEP-zTbDg.png" alt="gopher unchained" width="512" height="512"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fatal error: all goroutines are asleep - deadlock!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Damn, not again! 💀&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, that’s often the reaction if you’re not aware of all possible deadlocks that might happen.&lt;/p&gt;

&lt;p&gt;This article will help you prevent all possible deadlocks and understand their nature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Deadlocks in Go can cause your program to become unresponsive, leading to poor user experiences and potentially costly downtime. Even more, the Go compiler &lt;strong&gt;will NOT warn you&lt;/strong&gt; about these issues because deadlocks are a runtime phenomenon, not a compile-time error. This means that your code might compile and pass initial tests, only to fail under specific conditions in a production environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Understanding Deadlocks&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Deadlocks happen when a task gets stuck waiting for something that &lt;strong&gt;will never happen&lt;/strong&gt; , causing it to stop moving forward.&lt;/p&gt;

&lt;p&gt;This principle forms the foundation of your coding approach. Always inquire:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;- Will someone write something into the channel I’ve created?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Similarly, consider:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;- Am I trying to write to a channel without space?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;- Am I attempting to read from a channel that is currently empty?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to store something, you need a *&lt;em&gt;place ready to receive *&lt;/em&gt; it;&lt;/li&gt;
&lt;li&gt;to read something, you must ensure there’s something &lt;strong&gt;available to be read&lt;/strong&gt; ;&lt;/li&gt;
&lt;li&gt;to lock something, you must &lt;strong&gt;verify it’s free&lt;/strong&gt; and will be released when needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Deadlock types&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;All possible cases might be grouped into the next two groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Channel misuse&lt;/li&gt;
&lt;li&gt;Circular dependencies between goroutines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s consider them in more detail.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Channel Misuse&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Using channels incorrectly, like reading from an empty channel or writing to a full one, can make goroutines wait forever, causing deadlock. Let’s delve into and discuss all possible types within this category.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No receiver deadlock&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It occurs when a goroutine attempts to send data to an &lt;strong&gt;unbuffered&lt;/strong&gt; channel, but there is no corresponding receiver ready to receive that data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

func main() {
   // Create an unbuffered channel of integers
   goChannel := make(chan int)

   // Attempt to send the value 1 into the channel
   // This operation will block indefinitely because there's no receiver 
   // ready to receive the value
   goChannel &amp;lt;- 1
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This occurs specifically with unbuffered channels due to their &lt;strong&gt;lack of storage space&lt;/strong&gt;. The same code with a buffered channel won’t have deadlock.&lt;/p&gt;

&lt;p&gt;Got it! Let’s add a receiver and make it work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
   goChannel := make(chan int)
   goChannel &amp;lt;- 1

   // Attempt to read the value from the channel
   fmt.Println(&amp;lt;-goChannel)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait… What? Why does it still throw deadlock? 😡&lt;/p&gt;

&lt;p&gt;This is because channels &lt;strong&gt;aim to synchronize&lt;/strong&gt; two or more goroutines. And if one writes to the channel, there should be another goroutine that reads from the channel. Let’s fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
   "fmt"
   "time"
)

func main() {
   goChannel := make(chan int)
   goChannel &amp;lt;- 1

   go func() {
      // Attempt to read from the channel
      fmt.Println(&amp;lt;-goChannel)
   }()

   // Waiting for the goroutine to be executed
   time.Sleep(2 * time.Second)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait! Why still? 🤬&lt;/p&gt;

&lt;p&gt;Because, before putting value we need to make sure it will be received and &lt;strong&gt;reading must be called before writing&lt;/strong&gt;. Let’s switch places of read and write calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
   "fmt"
   "time"
)

func main() {
   goChannel := make(chan int)

   // Start reading from the channel
   go func() {
      // Attempt to read from the channel
      fmt.Println(&amp;lt;-goChannel)
   }()

   // Start writing to channel
   goChannel &amp;lt;- 1

   // Waiting for the goroutine to be executed
   time.Sleep(2 * time.Second)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, finally it works. 🥳&lt;/p&gt;

&lt;p&gt;This rule doesn’t apply to buffered channels since they have a &lt;strong&gt;place to store value&lt;/strong&gt; and one goroutine might handle both actions — read and write. The following code that previously didn’t work with unbuffered channel works now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
   "fmt"
)

func main() {
   // Define buffered channel
   goChannel := make(chan string, 1)
   // Attempt to write to the channel
   goChannel &amp;lt;- "hey!"
   // Attempt to read from the channel
   fmt.Println(&amp;lt;-goChannel)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No sender deadlock&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It occurs when a goroutine tries to read data from a channel, but &lt;strong&gt;no value will ever be sent&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

func main() {
   // Create a channel of integers
   goChannel := make(chan int)

   // Attempt to read a value from the channel
   // This operation will block indefinitely because there's no value
   // previously sent
   &amp;lt;- goChannel
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This logic applies to both buffered and unbuffered channels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing to a Full Channel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A deadlock can occur when a goroutine attempts to write to a buffered channel that is already full, and &lt;strong&gt;no other goroutine is available to read&lt;/strong&gt; from the channel. This leads to the write operation &lt;strong&gt;blocking indefinitely&lt;/strong&gt; , causing deadlock:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
   // Create a buffered channel with a capacity of 1
   ch := make(chan int, 1)

   // Send a value into the channel
   ch &amp;lt;- 1

   // Attempt to send another value into the channel
   // This will block because the channel is full and there's no receiver or place to store value
   ch &amp;lt;- 2

   fmt.Println("This line will never be printed")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reading from an Empty Channel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another case is when a goroutine attempts to read from an already &lt;strong&gt;emptied channel&lt;/strong&gt; , resulting in the read operation blocking indefinitely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
   // Create a buffered channel with a capacity of 1
   ch := make(chan string, 1)

   // Send a value into the channel 
   ch &amp;lt;- "first"

   // Attempt to read the value (will be printed)
   fmt.Println(&amp;lt;-ch)

   // Attempt to read the value again (will fail)
   fmt.Println(&amp;lt;-ch)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unclosed Channel Before Range&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The following code demonstrates one of the most common deadlocks that happens to developers when a goroutine iterates over a channel using a for-range loop, but the &lt;strong&gt;channel is never closed&lt;/strong&gt;. The for-range loop requires the channel to be closed to &lt;strong&gt;terminate iteration&lt;/strong&gt;. If the channel is not closed, the loop will block indefinitely, leading to a deadlock. Try to run with commented and uncommented &lt;strong&gt;&lt;em&gt;close(ch)&lt;/em&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func main() {
   // Create a buffered channel
   ch := make(chan int, 2)

   // Send some values into the channel
   ch &amp;lt;- 1
   ch &amp;lt;- 2

   // Close the channel to prevent deadlock
   // close(ch) // This line is intentionally commented out 
   // to demonstrate deadlock

   // Iterate over the channel using a for-range loop
   for val := range ch {
      fmt.Println(val)
   }

   fmt.Println("This line will be printed only if the channel was closed")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t leave channels opened 🫢&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Circular dependencies between goroutines&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Circular dependencies between goroutines occur when multiple goroutines are &lt;strong&gt;waiting on each other&lt;/strong&gt; to perform actions or exchange data, creating a situation where none of them can proceed without the others’ participation, leading to a deadlock.&lt;/p&gt;

&lt;p&gt;Correct managing dependencies between goroutines is crucial to prevent the mentioned deadlocks-situations. Let’s discuss the most common cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutex and Locking Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If one goroutine locks resource A first and then waits to lock resource B, while another goroutine locks resource B first and then waits to lock resource A, a deadlock can occur if &lt;strong&gt;both goroutines end up waiting indefinitely&lt;/strong&gt; for each other to release their locks.&lt;/p&gt;

&lt;p&gt;Try the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
 "fmt"
 "sync"
 "time"
)

func main() {
   // Declare two mutexes
   var mu1, mu2 sync.Mutex

   var wg sync.WaitGroup
   wg.Add(2)

   // Goroutine 1
   go func() {
      defer wg.Done()

      mu1.Lock()
      // Simulate some work or delay
      time.Sleep(1 * time.Second)
      mu2.Lock()

      mu2.Unlock()
      mu1.Unlock()
      fmt.Println("Goroutine 1: Unlocked")
   }()

   // Goroutine 2
   go func() {
      defer wg.Done()

      mu2.Lock()
      // Simulate some work or delay
      time.Sleep(1 * time.Second)
      mu1.Lock()

      mu1.Unlock()
      mu2.Unlock()
      fmt.Println("Goroutine 2: Unlocked")
   }()

   // Wait for all goroutines to finish
   wg.Wait()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent such deadlocks in Go, ensure that goroutines acquire locks in a &lt;strong&gt;consistent and mutually agreed order&lt;/strong&gt;. This prevents situations where one goroutine is waiting for a lock held by another goroutine, which is also waiting for a lock held by the first goroutine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deadlock Due to Misuse of WaitGroup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Missing to call &lt;strong&gt;&lt;em&gt;wg.Done()&lt;/em&gt;&lt;/strong&gt; can cause other goroutines waiting on the WaitGroup to block indefinitely, assuming that all tasks have been completed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
   "sync"
   "time"
)

func main() {
   var wg sync.WaitGroup

   wg.Add(1)

   go func() {
      // Uncomment wg.Done below to make it work
      // defer wg.Done()

      // Simulating some work
      time.Sleep(1 * time.Second)
   }()

   wg.Wait()
   // This would deadlock if wg.Done() was missing.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to always use &lt;strong&gt;&lt;em&gt;wg.Done()&lt;/em&gt;&lt;/strong&gt; when using &lt;em&gt;sync.WaitGroup&lt;/em&gt; to properly signal the completion of goroutines and avoid the deadlock.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;By implementing these best practices and understanding the scenarios that lead to deadlocks, you can significantly reduce the risk of encountering them in your Go programs.&lt;/p&gt;

&lt;p&gt;Consider using this checklist when developing your program in Golang:&lt;/p&gt;

&lt;p&gt;❗ Channel: Ensure no receiver or sender deadlock.&lt;br&gt;&lt;br&gt;
❗ Channel: Avoid writing to a full channel or reading from an empty one.&lt;br&gt;&lt;br&gt;
❗ Channel: Always close before using &lt;strong&gt;&lt;em&gt;range&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
❗ Mutex: Prevent deadlocks by managing locking order.&lt;br&gt;&lt;br&gt;
❗ WaitGroup: Use wg.Done() correctly to avoid blocking.&lt;/p&gt;

&lt;p&gt;Keep coding efficiently and watch out for those sneaky deadlocks! 🐈‍⬛&lt;/p&gt;

</description>
      <category>golangtutorial</category>
      <category>golangdevelopment</category>
      <category>go</category>
    </item>
    <item>
      <title>Algorithm Complexity with Go - Part 1</title>
      <dc:creator>Kostiantyn Lysenko</dc:creator>
      <pubDate>Sat, 04 May 2024 23:47:33 +0000</pubDate>
      <link>https://dev.to/kostiantyn_lysenko_5a13a9/algorithm-complexity-with-go-part-1-4hjg</link>
      <guid>https://dev.to/kostiantyn_lysenko_5a13a9/algorithm-complexity-with-go-part-1-4hjg</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--frA_q2Zr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AqZ2Twdb3-c00EhNoJL4zDg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--frA_q2Zr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AqZ2Twdb3-c00EhNoJL4zDg.jpeg" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Understanding algorithm complexity is essential for developing efficient software solutions. One key aspect of algorithm complexity is ordering growth types, which describe how the runtime of an algorithm scales with the size of the input.&lt;/p&gt;

&lt;p&gt;In this series of articles, we’ll explore several &lt;strong&gt;ordering growth types&lt;/strong&gt; , ranging from constant time to exponential time, and discuss their implications for algorithm design and performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Constant time complexity
&lt;/h3&gt;

&lt;p&gt;Imagine you have a super speedy delivery service that guarantees your package arrives in the same amount of time, no matter the distance, or your old laptop that infinitely opens as much as Paint and Adobe Photoshop. That’s constant time complexity in action!&lt;/p&gt;

&lt;p&gt;Constant time complexity means that the time taken to execute an operation &lt;strong&gt;remains constant&lt;/strong&gt; regardless of the input size.&lt;/p&gt;

&lt;p&gt;I’m going to show you this hackneyed example with &lt;strong&gt;accessing array&lt;/strong&gt;  &lt;strong&gt;element&lt;/strong&gt; (boring), but with Go benchmarks to give you visualization and make it clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func accessElement(arr []int, index int) int {
    return arr[index]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here no matter the size of the array, retrieving an element by index &lt;strong&gt;takes the same amount of time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s prove it in practice by running benchmarks with arrays of 10, 1000, 100000, 100000000 items in length.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "testing"
)

func accessElement(arr []int, index int) int {
    return arr[index]
}

func BenchmarkAccessElement10(b *testing.B) {
    arr := make([]int, 10) // Array size of 10
    for i := 0; i &amp;lt; len(arr); i++ {
        arr[i] = i
    }
    index := 5 // Fixed index for the array of size 10

    b.ResetTimer() // Isolate all code above
    for i := 0; i &amp;lt; b.N; i++ {
        _ = accessElement(arr, index)
    }
}

func BenchmarkAccessElement1000(b *testing.B) {
    arr := make([]int, 1000) // Array size of 1000
    for i := 0; i &amp;lt; len(arr); i++ {
        arr[i] = i
    }
    index := 500 // Fixed index for the array of size 1000

    b.ResetTimer()
    for i := 0; i &amp;lt; b.N; i++ {
        _ = accessElement(arr, index)
    }
}

func BenchmarkAccessElement100000(b *testing.B) {
    arr := make([]int, 100000) // Array size of 100000
    for i := 0; i &amp;lt; len(arr); i++ {
        arr[i] = i
    }
    index := 50000 // Fixed index for the array of size 100000

    b.ResetTimer()
    for i := 0; i &amp;lt; b.N; i++ {
        _ = accessElement(arr, index)
    }
}

func BenchmarkAccessElement100000000(b *testing.B) {
    arr := make([]int, 100000000) // Array size of 100000000
    for i := 0; i &amp;lt; len(arr); i++ {
        arr[i] = i
    }
    index := 5000000 // Fixed index for the array of size 100000000

    b.ResetTimer()
    for i := 0; i &amp;lt; b.N; i++ {
        _ = accessElement(arr, index)
    }
}

go test -bench .

goos: darwin
goarch: amd64
pkg: algorithms/complexity
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkAccessElement10
BenchmarkAccessElement10-4 1000000000 0.6557 ns/op
BenchmarkAccessElement1000
BenchmarkAccessElement1000-4 1000000000 0.6056 ns/op
BenchmarkAccessElement100000
BenchmarkAccessElement100000-4 1000000000 0.6613 ns/op
BenchmarkAccessElement100000000
BenchmarkAccessElement100000000-4 1000000000 0.6689 ns/op
PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F7M6Pl14--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Afc5dDX-BcbV7tfake5Fe0A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F7M6Pl14--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Afc5dDX-BcbV7tfake5Fe0A.jpeg" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice! Around 0.6 to 0.7 nanoseconds per operation — execution time remains unchanged even as the array size increases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why does it take the same time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because the &lt;strong&gt;operation is pretty simple&lt;/strong&gt;  — determine the memory address of the desired element based on its index.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How does it determine?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To calculate the memory address of an element, Go takes the base memory address of the array (the address of its first element) and &lt;strong&gt;adds an offset&lt;/strong&gt; to it. This offset is determined by the index of the desired element and the size of each element in the array.&lt;/p&gt;

&lt;p&gt;The offset for accessing the element at index i in the array is calculated using the formula offset = i * size_of_element where size_of_element represents the size of each element in bytes.&lt;/p&gt;

&lt;p&gt;Once the offset is calculated, it is added to the base memory address of the array and this results in the memory address of the desired element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;desired = base address + offset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, this calculation can be &lt;strong&gt;performed very quickly&lt;/strong&gt; by modern processors and the time taken to perform this calculation remains the same.&lt;/p&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;p&gt;If you wish to know another example — &lt;a href="https://en.wikipedia.org/wiki/Hash_table"&gt;retrieving data from a Hash Table&lt;/a&gt; is a good one.&lt;/p&gt;

</description>
      <category>timecomplexity</category>
      <category>algorithms</category>
      <category>benchmark</category>
      <category>go</category>
    </item>
  </channel>
</rss>
