title: Why is `np.sum(range(N))` very slow?
body: <p>I saw a <a href="https://www.youtube.com/watch?v=Qgevy75co8c" rel="noreferrer">video</a> about speed of loops in python, where it was explained that doing <code>sum(range(N))</code> is much faster than manually looping through <code>range</code> and adding the variables together, since the former runs in C due to built-in functions being used, while in the latter the summation is done in (slow) python. I was curious what happens when adding <code>numpy</code> to the mix. As I expected <code>np.sum(np.arange(N))</code> is the fastest, but <code>sum(np.arange(N))</code> and <code>np.sum(range(N))</code> are even slower than doing the naive for loop.</p>
  <p>Why is this?</p>
  <p>Here's the script I used to test, some comments about the supposed cause of slowing done where I know (taken mostly from the video) and the results I got on my machine (python 3.10.0, numpy 1.21.2):</p>
  <p><strong>updated script:</strong></p>
  <pre class="lang-py prettyprint-override"><code>import numpy as np
  from timeit import timeit
  
  N = 10_000_000
  repetition = 10
  
  def sum0(N = N):
      s = 0
      i = 0
      while i &lt; N: # condition is checked in python
          s += i
          i += 1 # both additions are done in python
      return s
  
  def sum1(N = N):
      s = 0
      for i in range(N): # increment in C
          s += i # addition in python
      return s
  
  def sum2(N = N):
      return sum(range(N)) # everything in C
  
  def sum3(N = N):
      return sum(list(range(N)))
  
  def sum4(N = N):
      return np.sum(range(N)) # very slow np.array conversion
  
  def sum5(N = N):
      # much faster np.array conversion
      return np.sum(np.fromiter(range(N),dtype = int))
  
  def sum5v2_(N = N):
      # much faster np.array conversion
      return np.sum(np.fromiter(range(N),dtype = np.int_))
  
  def sum6(N = N):
      # possibly slow conversion to Py_long from np.int
      return sum(np.arange(N))
  
  def sum7(N = N):
      # list returns a list of np.int-s
      return sum(list(np.arange(N)))
  
  def sum7v2(N = N):
      # tolist conversion to python int seems faster than the implicit conversion
      # in sum(list()) (tolist returns a list of python int-s)
      return sum(np.arange(N).tolist())
  
  def sum8(N = N):
      return np.sum(np.arange(N)) # everything in numpy (fortran libblas?)
  
  def sum9(N = N):
      return np.arange(N).sum() # remove dispatch overhead
  
  def array_basic(N = N):
      return np.array(range(N))
  
  def array_dtype(N = N):
      return np.array(range(N),dtype = np.int_)
  
  def array_iter(N = N):
      # np.sum's source code mentions to use fromiter to convert from generators
      return np.fromiter(range(N),dtype = np.int_)
  
  print(f&quot;while loop:         {timeit(sum0, number = repetition)}&quot;)
  print(f&quot;for loop:           {timeit(sum1, number = repetition)}&quot;)
  print(f&quot;sum_range:          {timeit(sum2, number = repetition)}&quot;)
  print(f&quot;sum_rangelist:      {timeit(sum3, number = repetition)}&quot;)
  print(f&quot;npsum_range:        {timeit(sum4, number = repetition)}&quot;)
  print(f&quot;npsum_iterrange:    {timeit(sum5, number = repetition)}&quot;)
  print(f&quot;npsum_iterrangev2:  {timeit(sum5, number = repetition)}&quot;)
  print(f&quot;sum_arange:         {timeit(sum6, number = repetition)}&quot;)
  print(f&quot;sum_list_arange:    {timeit(sum7, number = repetition)}&quot;)
  print(f&quot;sum_arange_tolist:  {timeit(sum7v2, number = repetition)}&quot;)
  print(f&quot;npsum_arange:       {timeit(sum8, number = repetition)}&quot;)
  print(f&quot;nparangenpsum:      {timeit(sum9, number = repetition)}&quot;)
  print(f&quot;array_basic:        {timeit(array_basic, number = repetition)}&quot;)
  print(f&quot;array_dtype:        {timeit(array_dtype, number = repetition)}&quot;)
  print(f&quot;array_iter:         {timeit(array_iter,  number = repetition)}&quot;)
  
  print(f&quot;npsumarangeREP:     {timeit(lambda : sum8(N/1000), number = 100000*repetition)}&quot;)
  print(f&quot;npsumarangeREP:     {timeit(lambda : sum9(N/1000), number = 100000*repetition)}&quot;)
  
  # Example output:
  #
  # while loop:         11.493371912998555
  # for loop:           7.385945574002108
  # sum_range:          2.4605720699983067
  # sum_rangelist:      4.509678105998319
  # npsum_range:        11.85120212900074
  # npsum_iterrange:    4.464334709002287
  # npsum_iterrangev2:  4.498494338993623
  # sum_arange:         9.537815956995473
  # sum_list_arange:    13.290120724996086
  # sum_arange_tolist:  5.231948580003518
  # npsum_arange:       0.241889145996538
  # nparangenpsum:      0.21876695199898677
  # array_basic:        11.736577274998126
  # array_dtype:        8.71628468400013
  # array_iter:         4.303306431000237
  # npsumarangeREP:     21.240833958996518
  # npsumarangeREP:     16.690092379001726
  
  </code></pre>