Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #83184 > unrolled thread

list comparison vs integer comparison, which is more efficient?

Started byaustin aigbe <eshikafe@gmail.com>
First post2015-01-03 15:19 -0800
Last post2015-01-04 13:24 +0100
Articles 9 — 6 participants

Back to article view | Back to comp.lang.python


Contents

  list comparison vs integer comparison, which is more efficient? austin aigbe <eshikafe@gmail.com> - 2015-01-03 15:19 -0800
    Re: list comparison vs integer comparison, which is more efficient? Chris Angelico <rosuav@gmail.com> - 2015-01-04 10:40 +1100
    Re: list comparison vs integer comparison, which is more efficient? Terry Reedy <tjreedy@udel.edu> - 2015-01-04 02:10 -0500
      Re: list comparison vs integer comparison, which is more efficient? austin aigbe <eshikafe@gmail.com> - 2015-01-04 03:20 -0800
        Re: list comparison vs integer comparison, which is more efficient? austin aigbe <eshikafe@gmail.com> - 2015-01-04 04:17 -0800
          Re: list comparison vs integer comparison, which is more efficient? Christian Gollwitzer <auriocus@gmx.de> - 2015-01-04 13:22 +0100
            Re: list comparison vs integer comparison, which is more efficient? Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-01-04 12:30 +0000
          Re: list comparison vs integer comparison, which is more efficient? Chris Angelico <rosuav@gmail.com> - 2015-01-04 23:25 +1100
          Re: list comparison vs integer comparison, which is more efficient? Jonas Wielicki <jonas@wielicki.name> - 2015-01-04 13:24 +0100

#83184 — list comparison vs integer comparison, which is more efficient?

Fromaustin aigbe <eshikafe@gmail.com>
Date2015-01-03 15:19 -0800
Subjectlist comparison vs integer comparison, which is more efficient?
Message-ID<d27586b9-017a-4519-a6bb-e0a1fac005d0@googlegroups.com>
Hi,

I am currently implementing the LTE physical layer in Python (ver 2.7.7).
For the qpsk, 16qam and 64qam modulation I would like to know which is more efficient to use, between an integer comparison and a list comparison:

Integer comparison: bit_pair as an integer value before comparison

    # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
    def mp_qpsk(self):
        r = []
        for i in range(self.nbits/2):
            bit_pair = (self.sbits[i*2] << 1) | self.sbits[i*2+1] 
            if bit_pair == 0:
                r.append(complex(1/math.sqrt(2),1/math.sqrt(2)))
            elif bit_pair == 1:
                r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
            elif bit_pair == 2:
                r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
            elif bit_pair == 3:
                r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
        return r

List comparison: bit_pair as a list before comparison

    # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
    def mp_qpsk(self):
        r = []
        for i in range(self.nbits/2):
            bit_pair = self.sbits[i*2:i*2+2] 
            if bit_pair == [0,0]:
                r.append(complex(1/math.sqrt(2),1/math.sqrt(2)))
            elif bit_pair == [0,1]:
                r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
            elif bit_pair == [1,0]:
                r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
            elif bit_pair == [1,1]:
                r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
        return r

Thanks

[toc] | [next] | [standalone]


#83186

FromChris Angelico <rosuav@gmail.com>
Date2015-01-04 10:40 +1100
Message-ID<mailman.17363.1420328417.18130.python-list@python.org>
In reply to#83184
On Sun, Jan 4, 2015 at 10:19 AM, austin aigbe <eshikafe@gmail.com> wrote:
> I would like to know which is more efficient to use, between an integer comparison and a list comparison:

You can test them with the timeit module, but my personal suspicion is
that any difference between them will be utterly and completely
dwarfed by all your sqrt(2) calls in the complex constructors. If you
break those out, and use a tuple instead of a list, you could write
this very simply and tidily:

    bits = {
        (0,0): complex(1/math.sqrt(2),1/math.sqrt(2)),
        (0,1): complex(1/math.sqrt(2),-1/math.sqrt(2)),
        (1,0): complex(-1/math.sqrt(2),1/math.sqrt(2)),
        (1,1): complex(-1/math.sqrt(2),-1/math.sqrt(2)),
    }
    # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
    def mp_qpsk(self):
        r = []
        for i in range(self.nbits/2):
            bit_pair = self.sbits[i*2:i*2+2]
            r.append(bits[tuple(bit_pair)])
        return r

At this point, your loop looks very much like a list comprehension in
full form, so you can make a simple conversion:

# From itertools recipes
# https://docs.python.org/3/library/itertools.html
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)
# Replace zip() with izip() for the Python 2 equivalent.

    def mp_qpsk(self):
        return [bits[pair] for pair in pairwise(self.sbits)]

How's that look? I don't care if it's faster or not, I prefer this form :)

ChrisA

[toc] | [prev] | [next] | [standalone]


#83189

FromTerry Reedy <tjreedy@udel.edu>
Date2015-01-04 02:10 -0500
Message-ID<mailman.17365.1420355468.18130.python-list@python.org>
In reply to#83184
On 1/3/2015 6:19 PM, austin aigbe wrote:

> I am currently implementing the LTE physical layer in Python (ver 2.7.7).
> For the qpsk, 16qam and 64qam modulation I would like to know which is more efficient to use, between an integer comparison and a list comparison:
>
> Integer comparison: bit_pair as an integer value before comparison
>
>      # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
>      def mp_qpsk(self):
>          r = []
>          for i in range(self.nbits/2):
>              bit_pair = (self.sbits[i*2] << 1) | self.sbits[i*2+1]
>              if bit_pair == 0:
>                  r.append(complex(1/math.sqrt(2),1/math.sqrt(2)))
>              elif bit_pair == 1:
>                  r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
>              elif bit_pair == 2:
>                  r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
>              elif bit_pair == 3:
>                  r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
>          return r
>
> List comparison: bit_pair as a list before comparison
>
>      # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
>      def mp_qpsk(self):
>          r = []
>          for i in range(self.nbits/2):
>              bit_pair = self.sbits[i*2:i*2+2]
>              if bit_pair == [0,0]:
>                  r.append()
>              elif bit_pair == [0,1]:
>                  r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
>              elif bit_pair == [1,0]:
>                  r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
>              elif bit_pair == [1,1]:
>                  r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
>          return r

Wrong question.  If you are worried about efficiency, factor out all 
repeated calculation of constants and eliminate the multiple comparisons.

sbits = self.sbits
a = 1.0 / math.sqrt(2)
b = -a
points = (complex(a,a), complex(a,b), complex(b,a), complex(b,b))
     complex(math.sqrt(2),1/math.sqrt(2))
def mp_qpsk(self):
     r = [points[sbits[i]*2 + sbits[i+1]]
         for i in range(0, self.nbits, 2)]
     return r

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#83194

Fromaustin aigbe <eshikafe@gmail.com>
Date2015-01-04 03:20 -0800
Message-ID<5499f48c-26e3-41c5-8735-2641fde5f35e@googlegroups.com>
In reply to#83189
On Sunday, January 4, 2015 8:12:10 AM UTC+1, Terry Reedy wrote:
> On 1/3/2015 6:19 PM, austin aigbe wrote:
> 
> > I am currently implementing the LTE physical layer in Python (ver 2.7.7).
> > For the qpsk, 16qam and 64qam modulation I would like to know which is more efficient to use, between an integer comparison and a list comparison:
> >
> > Integer comparison: bit_pair as an integer value before comparison
> >
> >      # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
> >      def mp_qpsk(self):
> >          r = []
> >          for i in range(self.nbits/2):
> >              bit_pair = (self.sbits[i*2] << 1) | self.sbits[i*2+1]
> >              if bit_pair == 0:
> >                  r.append(complex(1/math.sqrt(2),1/math.sqrt(2)))
> >              elif bit_pair == 1:
> >                  r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
> >              elif bit_pair == 2:
> >                  r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
> >              elif bit_pair == 3:
> >                  r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
> >          return r
> >
> > List comparison: bit_pair as a list before comparison
> >
> >      # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
> >      def mp_qpsk(self):
> >          r = []
> >          for i in range(self.nbits/2):
> >              bit_pair = self.sbits[i*2:i*2+2]
> >              if bit_pair == [0,0]:
> >                  r.append()
> >              elif bit_pair == [0,1]:
> >                  r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
> >              elif bit_pair == [1,0]:
> >                  r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
> >              elif bit_pair == [1,1]:
> >                  r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
> >          return r
> 
> Wrong question.  If you are worried about efficiency, factor out all 
> repeated calculation of constants and eliminate the multiple comparisons.
> 
> sbits = self.sbits
> a = 1.0 / math.sqrt(2)
> b = -a
> points = (complex(a,a), complex(a,b), complex(b,a), complex(b,b))
>      complex(math.sqrt(2),1/math.sqrt(2))
> def mp_qpsk(self):
>      r = [points[sbits[i]*2 + sbits[i+1]]
>          for i in range(0, self.nbits, 2)]
>      return r
> 
> -- 
> Terry Jan Reedy

Cool. Thanks a lot.

[toc] | [prev] | [next] | [standalone]


#83200

Fromaustin aigbe <eshikafe@gmail.com>
Date2015-01-04 04:17 -0800
Message-ID<7968fa1c-9b4c-4d17-9f71-1bbc55392921@googlegroups.com>
In reply to#83194
On Sunday, January 4, 2015 12:20:26 PM UTC+1, austin aigbe wrote:
> On Sunday, January 4, 2015 8:12:10 AM UTC+1, Terry Reedy wrote:
> > On 1/3/2015 6:19 PM, austin aigbe wrote:
> > 
> > > I am currently implementing the LTE physical layer in Python (ver 2.7.7).
> > > For the qpsk, 16qam and 64qam modulation I would like to know which is more efficient to use, between an integer comparison and a list comparison:
> > >
> > > Integer comparison: bit_pair as an integer value before comparison
> > >
> > >      # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
> > >      def mp_qpsk(self):
> > >          r = []
> > >          for i in range(self.nbits/2):
> > >              bit_pair = (self.sbits[i*2] << 1) | self.sbits[i*2+1]
> > >              if bit_pair == 0:
> > >                  r.append(complex(1/math.sqrt(2),1/math.sqrt(2)))
> > >              elif bit_pair == 1:
> > >                  r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
> > >              elif bit_pair == 2:
> > >                  r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
> > >              elif bit_pair == 3:
> > >                  r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
> > >          return r
> > >
> > > List comparison: bit_pair as a list before comparison
> > >
> > >      # QPSK - TS 36.211 V12.2.0, section 7.1.2, Table 7.1.2-1
> > >      def mp_qpsk(self):
> > >          r = []
> > >          for i in range(self.nbits/2):
> > >              bit_pair = self.sbits[i*2:i*2+2]
> > >              if bit_pair == [0,0]:
> > >                  r.append()
> > >              elif bit_pair == [0,1]:
> > >                  r.append(complex(1/math.sqrt(2),-1/math.sqrt(2)))
> > >              elif bit_pair == [1,0]:
> > >                  r.append(complex(-1/math.sqrt(2),1/math.sqrt(2)))
> > >              elif bit_pair == [1,1]:
> > >                  r.append(complex(-1/math.sqrt(2),-1/math.sqrt(2)))
> > >          return r
> > 
> > Wrong question.  If you are worried about efficiency, factor out all 
> > repeated calculation of constants and eliminate the multiple comparisons.
> > 
> > sbits = self.sbits
> > a = 1.0 / math.sqrt(2)
> > b = -a
> > points = (complex(a,a), complex(a,b), complex(b,a), complex(b,b))
> >      complex(math.sqrt(2),1/math.sqrt(2))
> > def mp_qpsk(self):
> >      r = [points[sbits[i]*2 + sbits[i+1]]
> >          for i in range(0, self.nbits, 2)]
> >      return r
> > 
> > -- 
> > Terry Jan Reedy
> 
> Cool. Thanks a lot.

Hi Terry,

No difference between the int and list comparison in terms of the number of calls(24) and time (0.004s). Main part is the repeated call to sqrt().

However, it took a shorter time (0.004s) with 24 function calls than your code (0.005s) which took just 13 function calls to execute.

Why is this?

Integer comparison profile result:
>>> p = pstats.Stats('lte_phy_mod.txt')
>>> p.strip_dirs().sort_stats(-1).print_stats()
Sun Jan 04 12:36:32 2015    lte_phy_mod.txt

         24 function calls in 0.004 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.004    0.004    0.004    0.004 lte_phy_layer.py:16(<module>)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:20(Scrambling)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:276(LayerMapping)

        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:278(Precoding)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:280(ResourceElementMapping)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:282(OFDMSignalGenerator)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:65(Modulation)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:71(__init__)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:87(mp_qpsk)
        1    0.000    0.000    0.000    0.000 {len}
        8    0.000    0.000    0.000    0.000 {math.sqrt}
        4    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}


<pstats.Stats instance at 0x028F3F08>
>>>

List comparison:
>>> import pstats
>>> p = pstats.Stats('lte_phy_mod2.txt')
>>> p.strip_dirs().sort_stats(-1).print_stats()
Sun Jan 04 12:57:24 2015    lte_phy_mod2.txt

         24 function calls in 0.004 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.004    0.004    0.004    0.004 lte_phy_layer.py:16(<module>)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:20(Scrambling)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:276(LayerMapping)

        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:278(Precoding)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:280(ResourceElementMapping)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:282(OFDMSignalGenerator)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:65(Modulation)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:71(__init__)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:87(mp_qpsk)
        1    0.000    0.000    0.000    0.000 {len}
        8    0.000    0.000    0.000    0.000 {math.sqrt}
        4    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}


<pstats.Stats instance at 0x025E3418>
>>>


Terry's code:

>>> import pstats
>>> p = pstats.Stats('lte_phy_mod3.txt')
>>> p.strip_dirs().sort_stats(-1).print_stats()
Sun Jan 04 13:04:51 2015    lte_phy_mod3.txt

         13 function calls in 0.005 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.004    0.004    0.005    0.005 lte_phy_layer.py:16(<module>)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:20(Scrambling)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:285(LayerMapping)

        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:287(Precoding)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:289(ResourceElementMapping)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:291(OFDMSignalGenerator)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:65(Modulation)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:71(__init__)
        1    0.000    0.000    0.000    0.000 lte_phy_layer.py:87(mp_qpsk)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {math.sqrt}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}


<pstats.Stats instance at 0x02783418>
>>>

[toc] | [prev] | [next] | [standalone]


#83201

FromChristian Gollwitzer <auriocus@gmx.de>
Date2015-01-04 13:22 +0100
Message-ID<m8bb91$2h7$1@dont-email.me>
In reply to#83200
Am 04.01.15 um 13:17 schrieb austin aigbe:
> However, it took a shorter time (0.004s) with 24 function calls than
your code (0.005s) which took just 13 function calls to execute.

> Why is this?

These times are way too short for conclusive results. Typically, the OS 
timer operates with a millisecond resolution. You need to run a 
benchmark at least for a second to get reliable information about 
timing. INstead of 24 times, call your function 20000 times in loop.

	Christian

[toc] | [prev] | [next] | [standalone]


#83203

FromMark Lawrence <breamoreboy@yahoo.co.uk>
Date2015-01-04 12:30 +0000
Message-ID<mailman.17371.1420374649.18130.python-list@python.org>
In reply to#83201
On 04/01/2015 12:22, Christian Gollwitzer wrote:
> Am 04.01.15 um 13:17 schrieb austin aigbe:
>> However, it took a shorter time (0.004s) with 24 function calls than
> your code (0.005s) which took just 13 function calls to execute.
>
>> Why is this?
>
> These times are way too short for conclusive results. Typically, the OS
> timer operates with a millisecond resolution. You need to run a
> benchmark at least for a second to get reliable information about
> timing. INstead of 24 times, call your function 20000 times in loop.
>
>      Christian
>

Maybe using a custom built tool such as 
https://docs.python.org/3/library/timeit.html#module-timeit ?

-- 
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

[toc] | [prev] | [next] | [standalone]


#83202

FromChris Angelico <rosuav@gmail.com>
Date2015-01-04 23:25 +1100
Message-ID<mailman.17370.1420374349.18130.python-list@python.org>
In reply to#83200
On Sun, Jan 4, 2015 at 11:17 PM, austin aigbe <eshikafe@gmail.com> wrote:
> However, it took a shorter time (0.004s) with 24 function calls than your code (0.005s) which took just 13 function calls to execute.
>
> Why is this?

That looks to me like noise in your stats. One ULP in timing stats?
Not something to base *anything* on.

ChrisA

[toc] | [prev] | [next] | [standalone]


#83235

FromJonas Wielicki <jonas@wielicki.name>
Date2015-01-04 13:24 +0100
Message-ID<mailman.17390.1420459599.18130.python-list@python.org>
In reply to#83200
On 04.01.2015 13:17, austin aigbe wrote
> Hi Terry,
> 
> No difference between the int and list comparison in terms of the number of calls(24) and time (0.004s). Main part is the repeated call to sqrt().
> 
> However, it took a shorter time (0.004s) with 24 function calls than your code (0.005s) which took just 13 function calls to execute.

How often did you run your measurement? 4ms is not a whole lot and can
easily be skewed by sudden system load or other noise. You should call
the function more often and/or repeat the measurement several times
before coming to a judgement (except, possibly, that it doesn’t matter).

cheers,
jwi

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web