The Myth of “One Pass = Faster”: Python Performance 101

 Is a single loop always faster than two?

Not necessarily.

Consider checking if two strings are anagrams — a classic O(n) problem.
Two popular solutions exist, but their real-world performance isn’t the same.


Option A: Fixed-Size Array (26 slots)


count = [0] * 26

for i in range(len(s)):

    count[ord(s[i]) - ord('a')] += 1

    count[ord(t[i]) - ord('a')] -= 1


for v in count:

    if v != 0:

        return False

return True


Option B: Dictionary Comparison


countS, countT = {}, {}

for i in range(len(s)):

    countS[s[i]] = 1 + countS.get(s[i], 0)

    countT[t[i]] = 1 + countT.get(t[i], 0)


return countS == countT


At first glance, this looks elegant:

  • One pass to build the dictionaries

  • One simple equality check at the end

However, this simplicity hides important runtime details.


The Hidden Cost

That final line:

countS == countT


is not a constant-time operation.

Under the hood, Python must:

  • Compare dictionary sizes

  • Iterate through keys

  • Perform hash lookups

  • Compare values for every key

This introduces an implicit loop — one that is easy to overlook when reasoning only at the surface level.


Why the Array Approach Often Wins

The fixed-size array solution benefits from:

  • Direct memory access

  • No hashing overhead

  • A predictable, constant-sized second pass (26 elements)

Even though both approaches are O(n) in Big-O terms, their constant factors differ significantly.

Big-O tells us how algorithms scale.
It does not tell us how fast they actually run.


The Broader Lesson

Performance is rarely about reducing the number of loops on paper.

It’s about understanding:

  • What abstractions cost

  • Where work is being done implicitly

  • How data structures behave at runtime

Readable code matters.
But understanding execution details is what separates working code from engineering-grade code.


Takeaway

A single pass is not automatically faster than two.
And clean abstractions are not free.

Real performance lives beyond the happy path 
in memory access patterns, implicit iterations, and runtime behavior.


Notes

  • This approach assumes lowercase English letters (a–z)

  • Constraints matter — always design with them in mind






Comments

Popular Posts