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


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

relative speed of incremention syntaxes (or "i=i+1" VS "i+=1")

Started byLaurent <laurent.payot@gmail.com>
First post2011-08-21 09:52 -0700
Last post2011-08-21 20:35 -0400
Articles 9 — 7 participants

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


Contents

  relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Laurent <laurent.payot@gmail.com> - 2011-08-21 09:52 -0700
    Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") woooee <woooee@gmail.com> - 2011-08-21 09:59 -0700
      Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Laurent <laurent.payot@gmail.com> - 2011-08-21 10:03 -0700
        Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Irmen de Jong <irmen@-NOSPAM-xs4all.nl> - 2011-08-21 19:14 +0200
          Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Hans Mulder <hansmu@xs4all.nl> - 2011-08-21 19:57 +0200
    Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Nobody <nobody@nowhere.com> - 2011-08-21 22:07 +0100
      Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Laurent Payot <laurent.payot@gmail.com> - 2011-08-21 16:49 -0700
        Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Terry Reedy <tjreedy@udel.edu> - 2011-08-21 22:15 -0400
      Re: relative speed of incremention syntaxes (or "i=i+1" VS "i+=1") Terry Reedy <tjreedy@udel.edu> - 2011-08-21 20:35 -0400

#11943 — relative speed of incremention syntaxes (or "i=i+1" VS "i+=1")

FromLaurent <laurent.payot@gmail.com>
Date2011-08-21 09:52 -0700
Subjectrelative speed of incremention syntaxes (or "i=i+1" VS "i+=1")
Message-ID<8c606fc1-0aa8-4113-b607-e46ad6f3d649@glegroupsg2000goo.googlegroups.com>
Hi Folks,

I was arguing with a guy who was sure that incrementing a variable i with "i += 1" is faster than "i = i + 1". I couldn't tell if he was right or wrong so I did a little benchmark with the very useful timeit module.
Here are the results on my little Linux Eeepc Netbook (using Python 3.2):


Computing, please wait...

Results for 1000000 times "i = i + 1":
0.37591004371643066
0.3827171325683594
0.37238597869873047
0.37305116653442383
0.3725881576538086
0.37294602394104004
0.3712761402130127
0.37357497215270996
0.371567964553833
0.37359118461608887
Total 3.7396 seconds.

Results for 1000000 times "i += 1":
0.3821070194244385
0.3802030086517334
0.3828878402709961
0.3823058605194092
0.3801591396331787
0.38340115547180176
0.3795340061187744
0.38153910636901855
0.3835160732269287
0.381864070892334
Total 3.8175 seconds.

==> "i = i + 1" is 2.08% faster than "i += 1".



I did many tests and "i = i + 1" always seems to be around 2% faster than "i += 1". This is no surprise as the += notation seems to be a syntaxic sugar layer that has to be converted to i = i + 1 anyway. Am I wrong in my interpretation?

Btw here's the trivial Python 3.2 script I made for this benchmark:


import timeit

r = 10
n = 1000000

s1 = "i = i + 1"
s2 = "i += 1"

t1 = timeit.Timer(stmt=s1, setup="i = 0")
t2 = timeit.Timer(stmt=s2, setup="i = 0")

print("Computing, please wait...")

results1 = t1.repeat(repeat=r, number=n)
results2 = t2.repeat(repeat=r, number=n)

print('\nResults for {} times "{}":'.format(n, s1))
sum1 = 0
for result in results1:
	print(result)
	sum1 += result
print("Total {:.5} seconds.".format(sum1))

print('\nResults for {} times "{}":'.format(n, s2))
sum2 = 0
for result in results2:
	print(result)
	sum2 += result
print("Total {:.5} seconds.".format(sum2))

print('\n==> "{}" is {:.3}% faster than "{}".'.format(s1,(sum2 / sum1) * 100 - 100, s2))



Comments are welcome...

[toc] | [next] | [standalone]


#11944

Fromwoooee <woooee@gmail.com>
Date2011-08-21 09:59 -0700
Message-ID<4af56750-aae1-4e93-8100-3a913f9901bf@a10g2000prn.googlegroups.com>
In reply to#11943
as the += notation seems to be a syntaxic sugar layer that has to be
converted to i = i + 1 anyway.

That has always been my understanding.  The faster way is to append to
a list as concatenating usually, requires the original string,
accessing an intermediate block of memory, and the memory for the
final string.
x_list.append(value)
to_string = "".join(x_list)

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


#11945

FromLaurent <laurent.payot@gmail.com>
Date2011-08-21 10:03 -0700
Message-ID<e57fb85a-8a3b-4572-b8b7-68d856a092ca@glegroupsg2000goo.googlegroups.com>
In reply to#11944
Well I agree with you about string concatenation, but here I'm talking about integers incrementation...

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


#11946

FromIrmen de Jong <irmen@-NOSPAM-xs4all.nl>
Date2011-08-21 19:14 +0200
Message-ID<4e513ceb$0$23863$e4fe514c@news2.news.xs4all.nl>
In reply to#11945
On 21-08-11 19:03, Laurent wrote:
> Well I agree with you about string concatenation, but here I'm talking about integers incrementation...

Seems the two forms are not 100% identical:

 >>> import dis
 >>> def f1(x):
...  x=x+1
...
 >>> def f2(x):
...  x+=1
...
 >>>
 >>> dis.dis(f1)
   2           0 LOAD_FAST                0 (x)
               3 LOAD_CONST               1 (1)
               6 BINARY_ADD
               7 STORE_FAST               0 (x)
              10 LOAD_CONST               0 (None)
              13 RETURN_VALUE
 >>> dis.dis(f2)
   2           0 LOAD_FAST                0 (x)
               3 LOAD_CONST               1 (1)
               6 INPLACE_ADD
               7 STORE_FAST               0 (x)
              10 LOAD_CONST               0 (None)
              13 RETURN_VALUE
 >>>


What the precise difference (semantics and speed) is between the 
BINARY_ADD and INPLACE_ADD opcodes, I dunno. Look in the Python source 
code or maybe someone knows it from memory :-)

Irmen

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


#11950

FromHans Mulder <hansmu@xs4all.nl>
Date2011-08-21 19:57 +0200
Message-ID<4e51471e$0$23880$e4fe514c@news2.news.xs4all.nl>
In reply to#11946
On 21/08/11 19:14:19, Irmen de Jong wrote:

> What the precise difference (semantics and speed) is between the
> BINARY_ADD and INPLACE_ADD opcodes, I dunno. Look in the Python source
> code or maybe someone knows it from memory :-)

There is a clear difference in semantics: BINARY_ADD always produces
a new object, INPLACE_ADD may modify its left-hand operand in situ
(if it's mutable).

Integers are immutable, so for integers the semantics are the same,
but for lists, for example, the two are different:

 >>> x = [2, 3, 5, 7]
 >>> y = [11, 13]
 >>> x+y
[2, 3, 5, 7, 11, 13]
 >>> x
[2, 3, 5, 7]		# x still has its original value
 >>> x += y
 >>> x
[2, 3, 5, 7, 11, 13]	# x is now modified
 >>>

For integers, I would not expect a measurable difference in speed.

Hope this helps,

-- HansM

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


#11964

FromNobody <nobody@nowhere.com>
Date2011-08-21 22:07 +0100
Message-ID<pan.2011.08.21.21.07.55.886000@nowhere.com>
In reply to#11943
On Sun, 21 Aug 2011 09:52:23 -0700, Laurent wrote:

> I did many tests and "i = i + 1" always seems to be around 2% faster
> than "i += 1". This is no surprise as the += notation seems to be a
> syntaxic sugar layer that has to be converted to i = i + 1 anyway. Am I
> wrong in my interpretation? 

It depends. If the value on the left has an __iadd__ method, that will be
called; the value will be updated in-place, so all references to that
object will be affected:

	> import numpy as np
	> a = np.zeros(3)
	> b = a
	> a
	array([ 0.,  0.,  0.])
	> b
	array([ 0.,  0.,  0.])
	> a += 1
	> a
	array([ 1.,  1.,  1.])
	> b
	array([ 1.,  1.,  1.])

If the value on the left doesn't have an __iadd__ method, then addition is
performed and the name is re-bound to the result:

	> a = a + 1
	> a
	array([ 2.,  2.,  2.])
	> b
	array([ 1.,  1.,  1.])

If you're writing code which could reasonably be expected to work with
arbitrary "numeric" values, you should decide which to use according to
whether in-place modification is appropriate rather than trivial
performance differences. If a difference of a few percent is significant,
Python is probably the wrong language in the first place.

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


#11973

FromLaurent Payot <laurent.payot@gmail.com>
Date2011-08-21 16:49 -0700
Message-ID<62dbc2b2-36bd-420c-a5f5-3a6aefcdc139@glegroupsg2000goo.googlegroups.com>
In reply to#11964
I made Python my language of choice because of its readability and simpleness, and not because of its speed. But it's always good to know what is the fastest sometimes when you don't want to write a module in C. So I was just wondering if there was a difference. There is, of a few percent. Anyway I will keep on using the 2% slower "i += 1" because for me that's less prone to errors because you write the variable only once, and that's more important than speed.

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


#11987

FromTerry Reedy <tjreedy@udel.edu>
Date2011-08-21 22:15 -0400
Message-ID<mailman.298.1313979609.27778.python-list@python.org>
In reply to#11973
On 8/21/2011 7:49 PM, Laurent Payot wrote:
> I made Python my language of choice because of its readability and
> simpleness, and not because of its speed. But it's always good to
> know what is the fastest sometimes when you don't want to write a
> module in C. So I was just wondering if there was a difference. There
> is, of a few percent. Anyway I will keep on using the 2% slower "i +=
> 1" because for me that's less prone to errors because you write the
> variable only once, and that's more important than speed.

For longer variable names, it is also easier and faster to read once one 
gets used to the idiom.

number_of_chars += 1 # versus
number_of_chars = number_of_chars + 1

Not repeating was a major reason for the addition.

-- 
Terry Jan Reedy

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


#11978

FromTerry Reedy <tjreedy@udel.edu>
Date2011-08-21 20:35 -0400
Message-ID<mailman.294.1313974278.27778.python-list@python.org>
In reply to#11964
On 8/21/2011 5:07 PM, Nobody wrote:

> If the value on the left has an __iadd__ method, that will be called;

Correct

> the value will be updated in-place,

Not necessarily correct. The target is rebound to the return from the 
__iadd__ method. Augmented *assignment* is always assignment. This trips 
up people who try

t = (1, [])
t[1] += [1,2] # which *does* extend the array, but raises
TypeError: 'tuple' object does not support item assignment
# instead of
t[1].extend([1,2]) # which extends without raising an error


*IF* (and only if) the target object is mutable, then the __iadd__ may 
optionally mutate the target object. But the rebinding takes place 
nonetheless. Numbers, the subject of this thread, are not mutable and 
are not 'updated in-place'

class test:
         def __init__(self, n):
                 self.n = n
         def __iadd__(self, other):
                 return test(self.n + other.n)
         def __repr__(self):
                 return repr(self.n)

r = test(1)
t = r
t += r
print(r, t)
# 1,2

That said, there is normally no reason to write an __ixxx__ method 
unless instances are mutable and the operation can be and is done in 
place therein. So the class above is for illustrative purposes only. A 
saner example is the following, which treats test examples as mutable 
number containers rather than as immutable surrogates.

class test:
         def __init__(self, n):
                 self.n = n
         def __add__(self, other):
                 return test(self.n + other.n)
         def __iadd__(self, other):
                 n = self.n + other.n
                 self.n = n
                 return n
         def __repr__(self):
                 return repr(self.n)

r = test(1)
t = r
t += r
print(r, t)
# 2 2

The interpreter cannot enforce that 'x += a' have the same effect as 'x 
= x+a', but it would break normal expectations to make the two different.


 > so all references to that object will be affected:

Only if the target object is mutable and is mutated by the optional 
augmented assignment __ixxx__ methods.

> 	>  import numpy as np
> 	>  a = np.zeros(3)

Numpy arrays meet the qualification above.

> If the value on the left doesn't have an __iadd__ method, then addition is
> performed and the name is re-bound to the result:

As is also done with the results of __ixxx__ methods.

-- 
Terry Jan Reedy

[toc] | [prev] | [standalone]


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


csiph-web