Advertisement
Craymichael

Python Map Performance Comparison

Jul 18th, 2019
634
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # =====================================================================
  2. # map_method.py - A python-timeit-opts file
  3. # Copyright (C) 2019  Zach Carmichael
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. # =====================================================================
  18. from __future__ import absolute_import
  19. from __future__ import division
  20. from __future__ import print_function
  21. from __future__ import unicode_literals
  22.  
  23. # noinspection PyCompatibility,PyUnresolvedReferences
  24. from builtins import (ascii, bytes, chr, dict, filter, hex, input,
  25.                       int, map, next, oct, open, pow, range, round,
  26.                       str, super, zip)
  27.  
  28. import sys
  29. import random
  30. import timeit
  31.  
  32. # noinspection PyUnresolvedReferences
  33. from operator import methodcaller
  34.  
  35. import numpy as np
  36.  
  37. import pandas as pd
  38. import seaborn as sns
  39. import matplotlib.pyplot as plt
  40.  
  41. sns.set(style='whitegrid')
  42.  
  43.  
  44. def main_import(*args):
  45.     if not args:
  46.         return None
  47.     return '\n'.join('from __main__ import {}'.format(arg) for arg in args)
  48.  
  49.  
  50. class DummyNum(object):
  51.     """Dummy class"""
  52.     __slots__ = 'n',
  53.  
  54.     def __init__(self, n):
  55.         self.n = n
  56.  
  57.     def add(self):
  58.         self.n += 5
  59.  
  60.  
  61. # Dict for product codes
  62. map_codes = dict(
  63.     map_lambda=(
  64.         'list(map(lambda x: x.add(), vals))',
  65.         None),
  66.     map_operator=(
  67.         'list(map(methodcaller("add"), vals))',
  68.         ('methodcaller',)),
  69.     map_comprehension=(
  70.         '[x.add() for x in vals]',
  71.         None),
  72. )
  73.  
  74. if len(sys.argv) > 1:
  75.     if len(sys.argv) > 2:
  76.         self_name = globals().get('__file__',  # Option 1
  77.                                   locals().get('__file__',  # Option 2
  78.                                                __name__))  # Option 3
  79.         sys.exit('Usage: {} [filename]'.format(self_name))
  80.     filename = sys.argv[1]
  81. else:
  82.     filename = None
  83.  
  84. if filename is None:
  85.     max_int = np.iinfo(np.int64).max
  86.  
  87.     # Array sizes
  88.     N = 50
  89.     sizes = np.logspace(0, 8, N, dtype=int)
  90.     repeats = np.logspace(4, 1, N, dtype=int)
  91.  
  92.     # Result storage
  93.     results = []
  94.  
  95.     # Begin heavy lifting
  96.     print('Python', sys.version)
  97.     for array_size, repeat in zip(sizes, repeats):
  98.         # Generate numbers (+1 as edge is non-inclusive)
  99.  
  100.         # Generate array_size numbers in range [-max_int - 1, max_int)
  101.         def gen_ints():
  102.             return [random.randint(-max_int - 1, max_int + 1)
  103.                     for _ in range(array_size)]
  104.  
  105.  
  106.         int_vals = gen_ints()
  107.         float_vals = list(map(float, int_vals))
  108.         # Cast 1st set to DummyNum
  109.         int_vals = list(map(DummyNum, int_vals))
  110.         float_vals = list(map(DummyNum, float_vals))
  111.  
  112.         # Evaluate each method, record results
  113.         for k_meth, (v_meth_code, v_meth_setup) in map_codes.items():
  114.  
  115.             if not isinstance(v_meth_code, (list, tuple)):
  116.                 v_meth_code = (v_meth_code, v_meth_code,)
  117.  
  118.             # Evaluate each dtype
  119.             for v_meth_code_dtype, dtype in zip(v_meth_code, ['int', 'float']):
  120.                 print('Timing {} with {}s for size {}'.format(
  121.                     k_meth, dtype, array_size))
  122.  
  123.                 v_meth_setup = v_meth_setup if v_meth_setup else tuple()
  124.  
  125.                 time_result = timeit.timeit(
  126.                     v_meth_code_dtype,
  127.                     setup=main_import('{}_vals as vals'.format(dtype),
  128.                                       *v_meth_setup),
  129.                     number=repeat
  130.                 )
  131.  
  132.                 results.append(dict(
  133.                     method=k_meth,
  134.                     time=time_result,
  135.                     dtype=dtype,
  136.                     size=array_size,
  137.                 ))
  138.  
  139.     # Plot results
  140.     py_v = sys.version_info
  141.     py_v_str = 'python{}.{}.{}'.format(py_v.major, py_v.minor, py_v.micro)
  142.  
  143.     df = pd.DataFrame(results)
  144.     df.to_csv('timeit_map_method_' + py_v_str + '.csv', index=False)
  145. else:
  146.     df = pd.read_csv(filename)
  147.  
  148. dash_styles = [
  149.     '',
  150.     (4.0, 1.5),
  151.     (1.0, 1.0),
  152.     (3.0, 1.0, 1.5, 1.0),
  153.     (5.0, 1.0, 1.0, 1.0),
  154.     (5.0, 1.0, 2.0, 1.0, 2.0, 1.0),
  155.     (2.0, 2.0, 3.0, 1.5),
  156.     (1.0, 2.5, 3.0, 1.2),
  157.     (1.0, 1.0, 1.0, 1.0, 1.0, 1.5, 5.0, 1.5),
  158.     (4.0, 1.5, 1.0, 1.0, 1.0, 1.0),
  159.     (2.0, 2.0, 1.0, 1.0),
  160. ]
  161.  
  162. g = sns.relplot(x='size', y='time', hue='method', style='method', col='dtype',
  163.                 kind='line', data=df, dashes=dash_styles)
  164. for ax in g.axes.flat:
  165.     ax.set_xscale('log')
  166.     ax.set_yscale('log')
  167. plt.show()
Advertisement
RAW Paste Data Copied
Advertisement