7
votes

Comment accélérer l'instanciation d'une grande collection d'objets?

Le code ci-dessous est extrêmement lent pour les tables de toute taille significative. (100, 1000, etc ...) Le coupable instanticule mes objets avec nouveau t () code>. Notez que ce n'est pas mon code finalisé, je viens de briser des parties de celui-ci afin de profiler plus facilement. L'instanciation et l'initialisation se produiront ensemble une fois que j'ai refactorisez le code.

Y a-t-il un moyen de accélérer cela? J'oublie probablement quelque chose de vraiment simple, ou peut-être que je suis désossé. Espérons que l'ancien. P> xxx pré>

éditer pour plus d'informations: p>

Le constructeur de tout modeltype code> ressemblera à ceci: p> xxx pré>

Le constructeur de tout modeltypeinfo code> définira simplement certaines valeurs de chaîne et de chaîne [], et que le seul travail de la classe est de fournir les valeurs Ensemble. P>

EDIT pour encore plus d'informations: strong> P>

Comme il semble être un sujet brûlant, voici ce que ma méthode ressemble à des réelles avant de sortir Construction d'objets et initialisation: P>

public static IList<T> ToList<T>(this DataTable table, ModelInfo modelInfo) where T : Model, new()
{
    var tempRepository = new Repository<T>(modelInfo);

    var list = new List<T>();
    foreach (DataRow row in table.Rows)
        list.Add(tempRepository.FromData(table, row));

    return list;
}


2 commentaires

OFF THUNIC: Pourquoi ne pas simplement créer une liste en premier lieu au lieu d'un t [] , puis tournez-le dans une liste? En outre, pourquoi pour au lieu de foreach ?


En réalité, que est ce que je fais. Je viens de cassé le code dans ce formulaire à des fins de démonstration. Vous savez, pour montrer où le code est lent, en utilisant aussi peu de magie que possible.


8 Réponses :


3
votes

Le titre de votre question suggère que cela a trait au fait que la méthode est générique. Alloue-t-il le même nombre d'objets sans génériques plus rapidement? Sinon, il doit être de faire avec n'importe quel travail dans votre constructeur. Pouvez-vous poster le code de constructeur?

édité Voici quelque chose que j'ai écrit dans un certain temps pour cache constructeurs dans une dynamicmethod, qui est très rapide:

dans votre classe: xxx

le corps de la méthode: xxx


3 commentaires

Correction du titre pour être moins spécifique.


Peut-être utiliser Func au lieu de déclarer un Constructelegate nommé?


Ça serait mieux. L'extrait est un peu vieux - écrit avant que je suis vraiment utilisé à l'aide de Func et d'action avec C # 3.



0
votes

testez-vous une version de sortie?
Est-ce que des tables.loop.comptez une propriété simple et pouvez-vous comparer de la hisser hors de la boucle?
Quel est le coût de l'instanciation t?
Crée-t-elle beaucoup d'allouer beaucoup de petits objets, de sorte que vous rencontrez quelques collections à déchets?


0 commentaires

2
votes

Avez-vous vraiment besoin d'une liste ou d'un iériférable être assez bon? Si tel est le cas, vous pouvez faire une création paresseuse / différée de vos objets:

public static IEnumerable<T> ToEnumerable<T>(this DataTable table, Func<DataRow, T> TFactory) 
{
    foreach (DataRow row in table.Rows)
    {
        yield return TFactory(row);
    }
}


3 commentaires

Le code d'origine était plus semblable à l'usine, et il existe une méthode pour générer un t basé sur un datarow. Le mot clé de rendement que je ne connaissais pas! Merci de me montrer ça. Malheureusement, j'ai besoin d'une iliste.


Vous pouvez simplement appeler .tolist () sur les résultats.


Droite, je dois avoir oublié. Que je suis bête. ;) J'ai fini par mettre en œuvre votre méthode iEnumerable de toute façon et nettoyé Tolist () sur Piggyback sur TOENMABLE (). J'aurai probablement besoin de ça, de toute façon.



13
votes

sous les couvertures, nouveau t () génère un appel à system.CreateInstance () , qui est (réfléchi) lent: XXX

Vous voudrez peut-être envisager de passer dans un délégué de la construction à la place.


8 commentaires

Ou, serait-il possible d'identifier le bon constructeur une seule fois, plutôt que n fois? Je pense que ce serait assez bon sans laider chaque appel à cette méthode.


CreateMegate nécessite une méthodeInfo qui ne correspond pas à ConstructorInfo. Au mieux, vous devez émettre une méthode d'emballage pour le constructeur et envelopper cela dans un délégué pour éviter le coup de réflexion à l'intérieur de la boucle.


Oui, vous pouvez appeler typeof (t) .getConstructor () une fois et constructorinfo.invoke () dans votre boucle. Cela devrait accélérer considérablement.


Le ciel vous aide si vous êtes sur le cadre compact - tout le coût de l'exécution lourd est dans la dernière invocation.


Consultez ma réponse ci-dessous pour un exemple d'enveloppement du constructeur dans un délégué à une dynamicmethod.


Un appel à Activator.createInstance n'est pas Toujours effectué uniquement pour les types de référence.


Néanmoins, c'est moche. Je n'utilise probablement jamais où T: nouveau () encore. Demander une méthode d'usine est simplement plus propre.


L'appel est probablement effectué pour des types de référence afin que seul un seul "Tout type de référence" soit émis et partagé parmi toutes les instanciations typées de référence du générique; Les types de valeurs génèrent des instanciations uniques, probablement en raison de l'effacement du type d'exécution.



0
votes

Pour afficher par exemple, cette méthode en C #:

.method public hidebysig instance !!T Method<.ctor T>() cil managed
{
.maxstack 2
.locals init (
    [0] !!T CS$1$0000,
    [1] !!T CS$0$0001)
L_0000: nop 
L_0001: ldloca.s CS$0$0001
L_0003: initobj !!T
L_0009: ldloc.1 
L_000a: box !!T
L_000f: brfalse.s L_001c
L_0011: ldloca.s CS$0$0001
L_0013: initobj !!T
L_0019: ldloc.1 
L_001a: br.s L_0021
L_001c: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
L_0021: stloc.0 
L_0022: br.s L_0024
L_0024: ldloc.0 
L_0025: ret 
}


0 commentaires

0
votes

Même si vous devez utiliser une liste, pourquoi créer d'abord le tableau?

public static IList<T> ToList<T>(this DataTable table) where T : Model, new()
{
    var list = new List<T>();
    foreach (DataRow dr in table.Rows) {
        T entity = new T();
        entity.Init(table, dr);
        list.Add(entity);
    }
    return list;
}


1 commentaires

C'est un code temporaire pour illustrer où se produit la lenteur. Lisez la question plus étroitement s'il vous plaît.



3
votes

Le problème est que l'expression nouveau t () code> utilise réellement la réflexion dans les coulisses. (Il appelle Activator.CreateInstance Code> ) Par conséquent, chaque appel va prendre du temps.


Une solution serait de contraindre T à la mise en oeuvre. Ensuite, vous pouvez écrire nouveau t () code> une fois et la cloner dans la boucle. Évidemment, vous ne pouvez faire que cela que si vous avez le contrôle total sur le modèle. P>


Une autre option serait de rendre la méthode prendre un délégué créateur, comme celui-ci: P>

static class CreatorFactory<T> where T : new() {
    public static readonly Func<T> Method = 
        Expression.Lambda<Func<T>>(Expression.New(typeof(T)).Compile();
}

public static IList<T> ToList<T>(this DataTable table) where T : Model {
    var entities = table.Rows.Select(r => CreatorFactory<T>.Method()).ToList();

    for (int i = 0; i < table.Rows.Count; i++)
        entities[i].Init(table, table.Rows[i]);

    return entities;
}


2 commentaires

Je vais juste suggérer cela. J'utilise une méthode similaire pour la construction de Treenodes.


Je parie que cela l'obtient jusqu'au prochain goulot d'étranglement (je peux parier parce que j'étais sur le point d'ajouter à peu près le même code dans ma réponse).



0
votes

Pour que quiconque se heurte à ce problème plus tard, ce message de blog était extrêmement utile pour moi: http://blogs.msdn.com/haibo_luo/archive/2005/11/17/494009.aspx

Voici les modifications que j'ai finies par faire à mon "usine" méthode. (Pas vraiment une usine appropriée, mais sert le but) xxx


0 commentaires