2010-04-10 9 views
2

On m'a confié la tâche de réécrire certaines bibliothèques écrites en C# afin qu'il n'y ait pas d'allocation une fois le démarrage terminé..NET DB requête sans allocations?

Je viens d'arriver à un projet qui fait des requêtes DB sur un OdbcConnection toutes les 30 secondes. J'ai toujours juste utilisé .ExecuteReader() qui crée un OdbcDataReader. Existe-t-il un modèle (comme le modèle de socket SocketAsyncEventArgs) qui vous permet de réutiliser votre propre OdbcDataReader? Ou une autre façon intelligente d'éviter les allocations?

Je n'ai pas pris la peine d'apprendre LINQ puisque tous les dbs au travail sont basés sur Oracle et la dernière fois que j'ai vérifié, il n'y avait pas de fournisseur Linq To Oracle officiel. Mais s'il y a un moyen de le faire dans Linq, je pourrais utiliser l'un des tiers.

Mise à jour:

Je ne pense pas que je spécifié clairement les raisons de l'exigence de non-alloc. Nous avons un thread critique en cours d'exécution et il est très important qu'il ne gèle pas. Ceci est pour une application de trading en temps quasi réel, et nous voyons un blocage de 100 ms pour certaines collections Gen 2. (J'ai aussi entendu parler de jeux écrits de la même manière en C#). Un thread d'arrière-plan effectue une vérification de conformité et s'exécute toutes les 30 secondes. Il fait une requête db en ce moment. La requête est assez lente (environ 500 ms pour revenir avec toutes les données), mais c'est correct car cela n'interfère pas avec le thread critique. Sauf si le thread de travail alloue de la mémoire, cela provoquera des GC qui gèlent tous les threads.

On m'a dit que toutes les bibliothèques (y compris celle-ci) ne peuvent pas allouer de mémoire après le démarrage. Que je sois d'accord ou non, c'est l'exigence des gens qui signent les chèques :).

Maintenant, il y a clairement des façons que je pourrais obtenir les données dans ce processus sans allocations. Je pourrais mettre en place un autre processus et le connecter à celui-ci en utilisant une socket. Les nouveaux sockets .NET 3.5 ont été spécifiquement optimisés pour ne pas être alloués du tout, en utilisant le nouveau pattern SocketAsyncEventArgs. (En fait, nous les utilisons pour se connecter à plusieurs systèmes et ne jamais voir de GC.) Ensuite, ayez un tableau d'octets pré-alloué qui lit à partir du socket et passe par les données, n'allouant aucune chaîne le long du chemin. (Je ne suis pas familier avec d'autres formes de IPC dans .NET, donc je ne suis pas sûr si les fichiers mappés en mémoire et les canaux nommés allouer ou non).

Mais s'il y a un moyen plus rapide d'obtenir cette requête sans allocation sans passer par tous les tracas, je préfère.

+0

Toutes les 30 secondes ... vous remarquez que l'effet des allocations sur de si longues périodes est très, très faible. – Dykam

+2

Hmm, pas d'allocations. Supprimez d'abord toutes les chaînes de vos tables DBase. Revenez quand vous avez terminé afin que nous puissions aborder les numéros 2 à 50. Vous devrez éventuellement passer en C++. –

Répondre

3

Vous ne pouvez pas réutiliser IDataReader (ou OdbcDataReader ou SqlDataReader ou toute classe équivalente). Ils sont conçus pour être utilisés avec une seule requête. Ces objets encapsulent un seul ensemble d'enregistrements, donc une fois que vous l'avez obtenu et l'avez itéré, il n'a plus aucun sens.

La création d'un lecteur de données est de toute façon une opération incroyablement bon marché, qui est infime par rapport au coût d'exécution de la requête. Je ne vois pas de raison logique à cette exigence de "non-attribution".

J'irais jusqu'à dire qu'il est presque impossible de réécrire une bibliothèque afin d'allouer pas de mémoire. Même quelque chose d'aussi simple que de boxer un entier ou d'utiliser une variable chaîne va allouer de la mémoire. Même s'il était en quelque sorte possible de réutiliser le lecteur (ce qui n'est pas le cas, comme je l'ai expliqué), il devrait encore remettre la requête à la base de données, ce qui nécessiterait des allocations de mémoire sous forme de requête sur le réseau, récupérer à nouveau les résultats, etc.

Éviter les allocations de mémoire n'est tout simplement pas un objectif pratique.Mieux vaut peut-être éviter spécifiques types d'allocations de mémoire si et quand vous déterminez qu'une opération spécifique utilise trop de mémoire.

+0

Le problème n'est pas qu'il faut beaucoup de temps pour allouer le lecteur de données lui-même. Comme vous le dites, il est éclipsé par l'heure de la requête. Et les requêtes et les allocations se produisent sur un thread de travail, donc nous ne nous soucions pas du temps qu'ils prennent. Mais au fil du temps, les allocations «bon marché» peuvent causer des CG qui gèlent tous les threads, y compris ceux critiques. Allouer aucune mémoire est probablement impossible. Mais voir par exemple: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=4215ab9e-4181-4526-823b-d364448188b2 –

+0

En d'autres termes, le but n'est pas d'allocations. Mais aucune allocation pendant la phase d'opérations continues de l'application (après le démarrage est terminé). –

+0

@Michael - mais comme vous l'avez dit, cette requête fait * partie * de votre "opération continue" et s'exécute toutes les 30 secondes. Les exigences de performance extrêmement strictes (12 000 TPS!) De cette application peuvent avoir nécessité des restrictions d'allocation de mémoire, mais dès que vous y insérez une requête de base de données, vous soufflez que hors de l'eau, la requête coûtera bien plus qu'un GC passer. – Aaronaught

2

Pour une telle exigence, êtes-vous sûr qu'un langage de haut niveau comme C# est votre choix?
Vous ne pouvez pas dire si les fonctions de la bibliothèque .NET que vous utilisez allouent de la mémoire en interne ou non. La norme ne le garantit pas, donc s'ils n'utilisent pas les allocations dans la version actuelle du framework .NET, ils peuvent commencer à le faire plus tard.

+0

True, ce n'est pas toujours clair si une fonction de bibliothèque alloue de la mémoire en fonction de sa signature. Mais vous pouvez toujours mettre l'appel de bibliothèque dans un nouveau projet et faire des appels de 100k pour voir s'il y a des GC. Bien que comme vous l'avez dit, ils peuvent le changer dans une version différente du cadre. C# n'était pas mon choix, mais certaines personnes essaient de suivre un modèle de conception sans allocation pendant la phase d'opération continue de l'application avec C# avec succès: http://www.microsoft.com/downloads/details.aspx?displaylang = fr & FamilyID = 4215ab9e-4181-4526-823b-d364448188b2 –

+1

@Michael, c'était pour un projet qui devait traiter 12 000 transactions par seconde sur une seule instance. Si vous mettez une seule requête de base de données, quelle que soit sa vitesse d'exécution, vous n'allez pas faire ce benchmark. Les requêtes de base de données sont ** plus coûteuses que les allocations de mémoire et les passes GC. – Aaronaught

+0

@Michael: Eh bien, le document que vous avez mentionné indique que l'approche comprenait une coopération étroite avec les spécialistes de la SP.De mon côté, je doute vraiment que la solution décrite dans l'article restera sans allocation, pour moi cela ressemble plus à un hack. Eh bien, vous pouvez essayer d'éviter d'utiliser les fonctions de la bibliothèque _à tous _... Mais votre question concerne la fonctionnalité liée à la base de données, donc je doute personnellement que de telles fonctions de bibliothèque plutôt compliquées n'alloueront pas en interne. – Vlad

1

Je vous suggère de profiler l'application pour déterminer où l'heure et/ou la mémoire sont dépensées. Ne devinez pas - vous ne devinerez que faux.

+0

N'est-ce pas, comme, règles 3-7 de l'optimisation? (Les deux premiers étant: "Do not!" Et "No, really, do not!") – SamB

+2

@SamB: c'est en fait 4-8. 3 est "Vous ne vouliez vraiment pas faire cela, alors défaites-le avant d'aggraver les choses". –