How many times have you looked at a piece of code and thought, “This should be faster”?

Recently, I went on a journey of optimizing a process that was taking 3 seconds and managed to bring it down to 0.0002 seconds. That’s a 150,000x improvement. But the real story isn’t just the final number—it’s what I broke along the way to get there.

The Challenge: Last-In, First-Out (LIFO)

The goal was simple: Reverse a string of 34,000 characters. To make it interesting, I benchmarked each approach by running the operation 1,000 times.

Phase 1: The Pythonic Way

Initially, I was doing what most of us do: focusing on readability and standard library functions. It worked, but at scale, it crawled.

class Stack: def init(self): self.items = []

def is_empty(self):
    #return len(self.items) == 0
    return not self.items
def push(self, item):
    self.items.append(item)
def pop(self):
    return self.items.pop()
def peek(self):
    return self.items[-1]
def size(self):
    return len(self.items)
def __str__(self):
    return str(self.items)

The Lesson:

I quickly discovered the “String Concatenation Trap.” Using += to build the result string in Python is O(n2) because strings are immutable; every addition creates a copy. By switching to a list-based accumulator and “".join(), I brought the time down significantly.

  • Final Python Time: ~3.16 seconds.

def reverse_string_slow(my_string): reversed_string = "”

# Create a new stack
s = Stack()
# Iterate through my_string and push the characters onto the stack
for char in my_string:
    s.push(char)
# Use a while loop with the exit condition that the stack is empty.
# Within this loop, update reversed_string with characters popped off the stack.

while not s.is_empty():
    reversed_string += s.pop()


#return reversed_string

The Faster approach def reverse_string_fast(my_string): reversed_string = ""

# Create a new stack
s = Stack()
# Iterate through my_string and push the characters onto the stack
for char in my_string:
    s.push(char)
# Use a while loop with the exit condition that the stack is empty.
# Within this loop, update reversed_string with characters popped off the stack.
accumulator = []
while not s.is_empty():
    accumulator.append(s.pop())
return "".join(accumulator) 

Phase 2: The Go Evolution (The Crash)

Moving to Go, I wanted to see how a compiled language handled the same Stack logic. I built a struct with Push and Pop methods.

The Lesson: I learned about “The Ghost Pop.” In my first attempt, I returned the top item but forgot to re-slice the underlying array.

Result: An infinite loop that allocated memory so fast it crashed my machine!
The Fix: s.items = s.items[:lastIdx] — strictly managing the slice length is the key to Go’s efficiency.

func (s *Stack) Pop() rune { if len(s.items) == 0 { return 0 } lastIdx := len(s.items) - 1 item := s.items[lastIdx] s.items = s.items[:lastIdx] return item }

The Pointer Swap version i followed to check the stats instead of Stack.

func (s *Stack) reverse() string { runes := []rune(s.str) n := len(runes) j := n - 1 for i := 0; i < n/2; i++ { runes[i], runes[j-i] = runes[j-i], runes[i] } return string(runes) }

Phase 3: The Ultimate Optimization (The Swap)

Finally, I moved away from the Stack entirely. Instead of moving data into a container, I used a Two-Pointer Swap directly on a slice of runes. This mirrored the indices from the outside-in.

Key Insights

- The "Allocation" Hidden Cost: While the Go Stack and the Go Swap were similar in speed, the Stack used 4.6x more memory and required 21x more interactions with the OS (allocations). In a high-traffic production environment, the Swap version is the clear winner for stability.
- Stateless vs. Stateful: The "Swap" method is stateless and O(1) extra space, making it a "Senior" approach to a "Junior" problem.
- The Performance Gap: Go finished in nanoseconds what Python finished in seconds. For data-heavy tasks, the difference is night and day.

Final Thoughts

Whether you are building a Neural Network or a simple string utility, understanding how your language manages memory is the difference between code that “works” and code that “scales.”

Don’t just write code—measure it.

#SoftwareEngineering #Golang #Python #Performance #Programming #DataStructures #ComputerScience

Originally published on LinkedIn