Home > Net >  Difference between list added to list and list multiplied by scalar
Difference between list added to list and list multiplied by scalar

Time:06-17

I wanted to duplicate the list by itself , so found these two approaches for doing this. This below code is running faster than the other statement. can you explain why first one executing so much faster than the second one.

First Approach :

   l = [1,2,3,...1000000]
    
    return l * 2 

Second Approach :

   l = [1,2,3,...1000000]
    
    return l   l  

edit : even this below code running slow as well

   l = [1,2,3,...1000000]
    
   l.extend(l) 

   return l

first code

second code

thrid code

CodePudding user response:

I believe most of the timing differences are due to improper timing. In particular, your last version using extend returns not the extended list but the result of the extend call, which is None. That means the list loses all references and gets deleted. And all its elements as well. And you include that deletion time. You also include the creation time. That's the same for all three, but it's an overhead that dilutes actual differences. I also suspect that you run your tests sequentially so that later ones suffer from having to delete the result of the previous one (and possibly from having less memory to work with).

I did three cycles where I measured the time components separately (with 10^7 elements and Python 3.10.2, see the full code at the end of this answer):

315 ms  create
157 ms  multiply
158 ms  add
 80 ms  extend
156 ms  delete

307 ms  create
157 ms  multiply
157 ms  add
 78 ms  extend
158 ms  delete

316 ms  create
168 ms  multiply
156 ms  add
 92 ms  extend
190 ms  delete

Your start list has a billion elements. They take 32 GB of memory. The list and the doubled list add 24 GB. If you indeed run them sequentially, the previous result is still in memory, so you have (32 16) (32 24) = 104 GB of data in memory. Unless you have that much actual free memory, you likely suffer from page faults. That would reduce the impact of the creation of the int objects and explain why that part is so much more significant in my measurements than in yours.

The *= has also been optimized recently, but only in Python 3.11, which hasn't been released yet, so I doubt you're using it. Also, that could only help explain why *= is faster for you than . Not why extend is so much slower for you than the others, when it should be faster. So I believe your time differences are simply mainly due to memory issues and improperly including deletion times (your snippet having to delete the result of your *= snippet, and your extend snippet having to delete both the result of the snippet and its own result).

Code (Try it online!):

n = 10**7

def create():
    global l
    l = list(range(n))

def multiply():
    return l * 2

def add():
    return l   l

def extend():
    l.extend(l)
    return l

def delete():
    global l
    del l

from timeit import default_timer as time

for _ in range(3):
    for f in create, multiply, add, extend, delete:
        t = time()
        r = f()
        t = time() - t
        del r
        print('= ms ' % (t * 1e3), f.__name__)
    print()

CodePudding user response:

To understand the differences between the different possibilities mentioned in the question you could use the dis to look at the corresponding bytecode operations.

Deduction concerning speed from bytecode is not trivial but could give a qualitative idea of what's going on behind the scenes.

from dis import dis

def by_scalar_multiplcation(lst, k):
    return lst * k

def by_addition(lst, lst2):
    return lst   lst2

def by_extension(lst, lst2):
    return lst.extend(lst2)

funcs = (by_scalar_multiplcation, by_addition, by_extension)

for f in funcs:
    print(f.__name__)
    print('='*len(f.__name__))
    dis(f)

Output

by_scalar_multiplcation
=======================
  9           0 LOAD_FAST                0 (lst)
              2 LOAD_FAST                1 (k)
              4 BINARY_MULTIPLY
              6 RETURN_VALUE
by_addition
===========
 12           0 LOAD_FAST                0 (lst)
              2 LOAD_FAST                1 (lst2)
              4 BINARY_ADD
              6 RETURN_VALUE
by_extension
============
 15           0 LOAD_FAST                0 (lst)
              2 LOAD_METHOD              0 (extend)
              4 LOAD_FAST                1 (lst2)
              6 CALL_METHOD              1
              8 RETURN_VALUE
  • Related