Flash Memory block write counting & thresholding
You can get an immediate speed-up by ditching the defaultdict(int), and using a bytearray(blockCount+1) instead.  Both have roughly \$O(1)\$ lookup time, but the latter has a much smaller constant factor.  In the former, each key must be hashed, then binned, then a linear search through the bin is required to find the correct key entry, if it exists, and creating it if it does not.  In the latter, the key is a direct index to the required memory.

This demonstrates a better than 3x improvement of bytearray over defaultdict:

&gt;&gt;&gt; from collections import defaultdict
&gt;&gt;&gt; from timeit import timeit
&gt;&gt;&gt; def original():
    d = defaultdict(int)
    for i in range(1000000):
        d[i] += 1


&gt;&gt;&gt; def using_bytearray():
    b = bytearray(1000000)
    for i in range(1000000):
        b[i] += 1


&gt;&gt;&gt; timeit(original, number=100)
25.083420443999984
&gt;&gt;&gt; timeit(using_bytearray, number=100)
7.779039941000008


Not only is the bytearray faster, it is also more memory efficient.  The bytearray uses just 57 bytes of overhead to store an array of 1 million bytes.  In contrast, the defaultdict weighs in at over 40x more memory ...

&gt;&gt;&gt; sys.getsizeof(b)
1000057
&gt;&gt;&gt; sys.getsizeof(d)
41943144


... but the real kicker is that is just the size of the dictionary's binning structures.  It excludes the size of all of the keys and values stored in the dictionary; those are harder to total as many small integers are interned and don't use additional storage allocation, and duplicate values may point to the same object.

Note: If threshold can exceed 255, you’d need to use array.array instead, with the appropriate type code for the largest rewrite count you’ll experience.



Of course, this is still the wrong approach.  With writes = [[0, 999999], [1, 1000000]], it should be obvious that block 0 is written once, blocks 1 through 999999 are written twice, and block 1000000 is written once.  There is no need to loop through the million indices; we just need to consider the end-points of each range.

So just store the starting &amp; ending points, and the number of times each has been seen.  With a sorted array of indices of where the transitions occur, you should be able to iterate over these transition points, and construct your diagnostic list.

Implementation left to student.

Bonus: it is possible use one simple Dict[int,int] structure to store both starting and ending counts, if you think about it the right way.