Are min() and max() thread-safe?

Paul McGuire ptmcg at austin.rr.com
Thu Sep 17 05:01:05 EDT 2009


On Sep 16, 11:33 pm, Steven D'Aprano
<ste... at REMOVE.THIS.cybersource.com.au> wrote:
> I have two threads, one running min() and the other running max() over
> the same list. I'm getting some mysterious results which I'm having
> trouble debugging. Are min() and max() thread-safe, or am I doing
> something fundamentally silly by having them walk over the same list
> simultaneously?
>

If you are calculating both min and max of a sequence, here is an
algorithm that can cut your comparisons by 25% - for objects with rich/
time-consuming comparisons, that can really add up.

import sys
if sys.version[0] == 2:
    range = xrange

def minmax(seq):
    if not seq:
        return None, None
    min_ = seq[0]
    max_ = seq[0]
    seqlen = len(seq)
    start = seqlen % 2
    for i in range(start,seqlen,2):
        a,b = seq[i],seq[i+1]
        if a > b:
            a,b = b,a
        if a < min_:
            min_ = a
        if b > max_:
            max_ = b
    return min_,max_

With this test code, I verified that the comparison count drops from
2*len to 1.5*len:

if __name__ == "__main__":

    import sys
    if sys.version[0] == 2:
        range = xrange

    import random

    def minmax_bf(seq):
        # brute force, just call min and max on sequence
        return min(seq),max(seq)

    testseq = [random.random() for i in range(1000000)]

    print minmax_bf(testseq)
    print minmax(testseq)

    class ComparisonCounter(object):
        tally = 0
        def __init__(self,obj):
            self.obj = obj
        def __cmp__(self,other):
            ComparisonCounter.tally += 1
            return cmp(self.obj,other.obj)
        def __getattr__(self,attr):
            return getattr(self.obj, attr)
        def __str__(self):
            return str(self.obj)
        def __repr__(self):
            return repr(self.obj)

    testseq = [ComparisonCounter(random.random()) for i in range
(10001)]

    print minmax_bf(testseq)
    print ComparisonCounter.tally
    ComparisonCounter.tally = 0

    print minmax(testseq)
    print ComparisonCounter.tally


Plus, now that you are finding both min and max in a single pass
through the sequence, you can wrap this in a lock to make sure of the
atomicity of your results.

(Just for grins, I also tried sorting the list and returning elements
0 and -1 for min and max - I got numbers of comparisons in the range
of 12X to 15X the length of the sequence.)

-- Paul



More information about the Python-list mailing list