Lors de la manipulation d'objets .NET, il est parfois nécessaire de choisir entre les fonctions BuiltIn Python et les méthodes d'extension LINQ. Cependant, la meilleure approche ne dépend pas toujours de la méthode à laquelle on pense initialement, surtout avec l'évolution récente de .NET.
Dans le cadre de cet article, nous allons effectuer un test comparatif entre quelques méthodes BuiltIn Python et les méthodes d'extension LINQ avec Dynamo 3.1.0 (.NET 8). Il est important de noter que le choix optimal dépendra du contexte spécifique et des objectifs finaux.
Dans notre test, nous avons évalué les performances des méthodes BuiltIn
Python par rapport aux méthodes d'extension LINQ. Les résultats, que nous
détaillerons ci-dessous, peuvent fournir des informations précieuses sur
l'efficacité de chaque approche.
Tout dépendra de votre contexte
et du but final, mais ce n'est pas forcément à la méthode à laquelle on
pense qui est forcément la meilleure.
-
Dans certains cas, l'utilisation d'Énumérables et de méthodes
d'extension LINQ peut s'avérer avantageuse.
-
Lorsque le nombre d'objets est très élevé, la conversion d'un
Énumérable en Net Collection avec la méthode
ToList()
est à éviter si possible (idem avec la méthodeToElements()
d'un objetFilteredElementCollector
de l'API Revit)
-
Parfois, les méthodes Python auront de meilleures performances (exemple
min()
etmax()
)
-
L'utilisation du module python
operator
, présente des avantages.
-
Dans le cas de traitement de données ou de géométries, il pourrait
être plus utile d'utiliser les bibliothèques Python
numpy
et/oupandas
Note:
En anticipant le ciblage d'IronPython3 pour Net 8, nous pouvons envisager des améliorations potentielles en termes de performances avec LINQ.
Voici un exemple de code Python pour effectuer des Tests
#Load the Python Standard and DesignScript Libraries
import sys
print(sys.implementation.name + " : " + sys.version)
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
net_clr_runtime = "Net Runtime : " + str(System.Environment.Version)
ipy3_assembly = System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(lambda a : a.GetName().Name == 'IronPython.Modules')
assembly_ipy3_Directory = System.IO.Path.GetDirectoryName( ipy3_assembly.Location )
parent_ipy3_Directory = System.IO.Directory.GetParent(assembly_ipy3_Directory).FullName
print(parent_ipy3_Directory)
sys.path.append(parent_ipy3_Directory + "\\lib")
import datetime
import operator
def decotimeit(n_try):
def timeit(func):
def timeit_wrapper(*args, **kwargs):
a = datetime.datetime.now()
for i in range(n_try):
result = func(*args, **kwargs)
b = datetime.datetime.now() - a
mean_time = b.microseconds / n_try
if "py" in func.__name__:
print("Python Function {} with {} points, average time {} microseconds ({} tests) | {}".format(func.__name__, len(args[0]), mean_time, n_try, net_clr_runtime))
else:
print("LINQ Function {} with {} points, average time {} microseconds ({} tests) | {}".format(func.__name__, len(args[0]), mean_time, n_try, net_clr_runtime))
return result
return timeit_wrapper
return timeit
@decotimeit(n_try = 20)
def Test_AddVector_NetLinq(lst_pts):
return lst_pts.Select(lambda pt : pt.Add(Vector.ByCoordinates(1,1,1))).AsEnumerable()
@decotimeit(n_try = 20)
def Test_AddVector_py(lst_pts):
return [pt.Add(Vector.ByCoordinates(1,1,1)) for pt in lst_pts]
@decotimeit(n_try = 20)
def Test_OrderBy_NetLinq(lst_pts):
return lst_pts.OrderBy(lambda pt : pt.X)
@decotimeit(n_try = 20)
def Test_OrderBy_py(lst_pts):
lst_pts.sort(key = lambda pt : pt.X)
return lst_pts
@decotimeit(n_try = 20)
def Test_OrderBy_attrgetter_py(lst_pts):
keyfun= operator.attrgetter("X")
lst_pts.sort(key = keyfun)
return lst_pts
@decotimeit(n_try = 20)
def Test_MinBy_NetLinq(lst_pts):
return lst_pts.MinBy(lambda pt : (pt.Z))
@decotimeit(n_try = 20)
def Test_MinBy_py(lst_pts):
return min(lst_pts, key = lambda pt : (pt.Z))
@decotimeit(n_try = 20)
def Test_MinZ_NetLinq(lst_pts):
return lst_pts.MinBy(lambda pt : pt.Z).Z
@decotimeit(n_try = 20)
def Test_MinZ_py(lst_pts):
return min(lst_pts, key = lambda pt : (pt.Z)).Z
lst_pts = IN[0]
print("-" * 10)
result_add_linq = Test_AddVector_NetLinq(lst_pts)
result_add_py = Test_AddVector_py(lst_pts)
print("-" * 10)
result_sort_linq = Test_OrderBy_NetLinq(lst_pts)
result_sort_py = Test_OrderBy_py(lst_pts)
result_sort_attrgetter_py = Test_OrderBy_attrgetter_py(lst_pts)
print("-" * 10)
result_minZ_linq = Test_MinZ_NetLinq(lst_pts)
result_minZ_py = Test_MinZ_py(lst_pts)
OUT = [result_add_linq.ElementAt(i) for i in range(5)], \
result_add_py[:5], \
[result_sort_linq.ElementAt(i) for i in range(5)], \
result_sort_py[:5], \
result_minZ_linq, \
result_minZ_py
Pour ceux qui auraient des interrogations sur la fonction décoratrice
decotimeit
, vous pouvez consulter mon
précédent article à ce sujet
| Test | Méthode | Nbr Points Testés | Temps Moyen (Microsecondes) | Nombre de Tests | .NET Runtime |
|--------------------------------------------|---------------------------------|-------------------|-----------------------------|------------------|--------------|
| LINQ Function Test_AddVector_NetLinq | Net LINQ | 300 000 | 50.0 | 20 | 8.0.0 |
| Python Function Test_AddVector_py | BuiltIn Python | 300 000 | 10550.0 | 20 | 8.0.0 |
| | | | | | |
| LINQ Function Test_OrderBy_NetLinq | Net LINQ | 300 000 | 50.0 | 20 | 8.0.0 |
| Python Function Test_OrderBy_py | BuiltIn Python | 300 000 | 12200.0 | 20 | 8.0.0 |
| Python Function Test_OrderBy_attrgetter_py | Python Operator | 300 000 | 9200.0 | 20 | 8.0.0 |
| | | | | | |
| LINQ Function Test_MinZ_NetLinq | Net LINQ | 300 000 | 18450.0 | 20 | 8.0.0 |
| Python Function Test_MinZ_py | BuiltIn Python | 300 000 | 8800.0 | 20 | 8.0.0 |
0 commentaires:
Enregistrer un commentaire