Pourquoi est-il difficile de garantir l'efficacité lors de l'utilisation des bibliothèques?

10

Tout petit traitement de base de données peut être facilement abordé par des scripts Python / Perl / ..., qui utilisent des bibliothèques et / ou même des utilitaires du langage lui-même. Cependant, en ce qui concerne les performances, les gens ont tendance à tendre la main pour les langages C / C ++ / bas niveau. La possibilité d'adapter le code aux besoins semble être ce qui rend ces langages si attrayants pour BigData - que ce soit en ce qui concerne la gestion de la mémoire, le parallélisme, l'accès au disque ou même des optimisations de bas niveau (via des assemblys au niveau C / C ++).

Bien sûr, un tel ensemble d'avantages ne serait pas gratuit: l'écriture du code, et parfois même la réinvention de la roue , peuvent être assez coûteuses / fastidieuses. Bien qu'il existe de nombreuses bibliothèques disponibles, les utilisateurs sont enclins à écrire le code eux-mêmes chaque fois qu'ils ont besoin d' accorder des performances. Qu'est-ce qui empêche les assertions de performances d'utiliser des bibliothèques lors du traitement de grandes bases de données?

Par exemple, considérons une entreprise qui explore continuellement des pages Web et analyse les données collectées. Pour chaque fenêtre coulissante, différents algorithmes d'exploration de données sont exécutés sur les données extraites. Pourquoi les développeurs abandonneraient-ils l'utilisation des bibliothèques / frameworks disponibles (que ce soit pour l'exploration, le traitement de texte et l'exploration de données)? L'utilisation de choses déjà implémentées non seulement allégerait le fardeau du codage de l'ensemble du processus, mais permettrait également de gagner beaucoup de temps.

En un seul coup :

  • qu'est-ce qui fait que l'écriture du code par soi-même est une garantie de performance?
  • pourquoi est-il risqué de s'appuyer sur des frameworks / bibliothèques alors que vous devez assurer des performances élevées?
Rubens
la source
1
Pouvez-vous clarifier la question exacte? Peut-être que certaines réponses possibles que vous avez en tête peuvent également vous aider.
Amir Ali Akbari
@AmirAliAkbari SeanOwen a posté une réponse et j'ai remarqué le manque de spécificité de ma question. J'ai ajouté un commentaire à son post. S'il vous plaît, n'hésitez pas à suggérer des améliorations sur le post - je prévois de le supprimer, sinon.
Rubens

Réponses:

4

Ayant fait le jeu de réécriture encore et encore (et toujours le faire), ma réaction immédiate a été l' adaptabilité .

Alors que les frameworks et les bibliothèques disposent d'un énorme arsenal de routines (éventuellement interconnectables) pour les tâches standard, leur propriété de framework interdit souvent (toujours?) Les raccourcis. En fait, la plupart des cadres ont une sorte d'infrastructure de base autour de laquelle une couche de base de fonctionnalités de base est implémentée. Des fonctionnalités plus spécifiques utilisent la couche de base et sont placées dans une deuxième couche autour du noyau.

Maintenant, par raccourcis, je veux dire passer directement d'une routine de deuxième couche à une autre routine de deuxième couche sans utiliser le noyau. Un exemple typique (de mon domaine) serait les horodatages: vous avez une source de données horodatée d'une certaine sorte. Jusqu'à présent, le travail consiste simplement à lire les données sur le fil et à les transmettre au noyau afin que votre autre code puisse s'en régaler.

Maintenant, votre industrie modifie le format d'horodatage par défaut pour une très bonne raison (dans mon cas, ils sont passés de l'heure Unix à l'heure GPS). À moins que votre cadre ne soit spécifique à l'industrie, il est très peu probable qu'ils soient prêts à changer la représentation de base du temps, donc vous finissez par utiliser un cadre qui fait presque ce que vous voulez. Chaque fois que vous accédez à vos données, vous devez d'abord les convertir au format de l'heure de l'industrie, et chaque fois que vous souhaitez les modifier, vous devez les reconvertir en tout ce que le cœur juge approprié. Il n'y a aucun moyen de transférer des données directement de la source vers un récepteur sans double conversion.

C'est là que vos cadres fabriqués à la main brillent, ce n'est qu'un changement mineur et vous modélisez le monde réel alors que tous les autres cadres (non spécifiques à l'industrie) auront désormais un inconvénient en termes de performances.

Au fil du temps, l'écart entre le monde réel et le modèle s'accumulera. Avec un cadre impromptu vous seriez bientôt face à des questions comme: Comment puis - je représenter thisdans thatou comment routine make Xaccepter / produits Y.

Jusqu'à présent, il ne s'agissait pas de C / C ++. Mais si, pour une raison quelconque, vous ne pouvez pas modifier le cadre, c'est-à-dire que vous devez accepter une double conversion des données pour passer d'un bout à l'autre, vous utiliserez généralement quelque chose qui minimise les frais généraux supplémentaires. Dans mon cas, il vaut mieux laisser un convertisseur TAI-> UTC ou UTC-> TAI en C brut (ou un FPGA). Il n'y a aucune élégance possible, aucune structure de données intelligente profonde qui rend le problème trivial. C'est juste une instruction de commutateur ennuyeuse, et pourquoi ne pas utiliser un langage dont les compilateurs sont bons pour optimiser exactement cela?

hroptatyr
la source
1
+1 C'est peut-être de ma faute si je n'ai pas été très clair dans mon message, alors d'autres ne l'avaient pas compris auparavant. C'est sûrement le genre de réponse que je cherchais. Merci.
Rubens
7

Je ne pense pas que tout le monde atteigne C / C ++ lorsque les performances sont un problème.

L'avantage d'écrire du code de bas niveau est d'utiliser moins de cycles CPU, ou parfois moins de mémoire. Mais je noterais que les langues de niveau supérieur peuvent appeler des langues de niveau inférieur, et le font, pour obtenir une partie de cette valeur. Les langages Python et JVM peuvent le faire.

Le scientifique des données utilisant, par exemple, scikit-learn sur son bureau, appelle déjà des routines natives fortement optimisées pour faire le calcul des nombres. Il est inutile d'écrire un nouveau code pour la vitesse.

Dans le contexte du «big data» distribué, vous êtes plus généralement goulot d'étranglement sur le mouvement des données: transfert réseau et E / S. Le code natif n'aide pas. Ce qui aide n'est pas d'écrire le même code pour fonctionner plus rapidement, mais d'écrire du code plus intelligent.

Les langages de niveau supérieur vous permettront d'implémenter des algorithmes distribués plus sophistiqués en un temps de développement donné que C / C ++. À grande échelle, l'algorithme plus intelligent avec un meilleur mouvement des données battra le code natif stupide.

Il est également généralement vrai que le temps de développement et les bogues coûtent plus cher que le nouveau matériel. Un an de temps d'un développeur senior peut coûter 200 000 $ à pleine charge; sur une année qui loue également des centaines de serveurs en temps de calcul. Dans la plupart des cas, cela peut ne pas avoir de sens de se soucier d'optimiser plutôt que d'y jeter plus de matériel.

Je ne comprends pas le suivi sur "accorder" et "désactiver" et "affirmer"?

Sean Owen
la source
Désolé pour le malentendu. Mon intention était d'apporter des réponses concernant l'importance d'avoir le contrôle sur une application, et comment ce contrôle est relâché par les bibliothèques. Bien sûr, vous pouvez supposer des choses à leur sujet (les gens ne réécrivent pas normalement pthreads), mais si les données changent (charge, débit, ...), vous devrez peut-être accéder à la source de la bibliothèque pour garantir les performances. Et oui, ce n'est pas nécessairement C / C ++ - bien que ce soient généralement les langues choisies pour hpc. Puis-je supprimer ma question ou souhaitez-vous la changer en quelque chose de plus spécifique? J'accepte toutes suggestions pour l'améliorer.
Rubens
1
Non, c'est une bonne question, vous pouvez refléter vos commentaires ici en modifiant la question si vous le souhaitez.
Sean Owen
Veuillez vérifier si la question a du sens maintenant. J'ai ajouté un petit boîtier afin de le rendre plus simple. Dans le cas où vous souhaitez ajouter une certaine considération dans la question, n'hésitez pas à la modifier.
Rubens
4

Comme tout ce que nous savons, dans le monde numérique, il existe de nombreuses façons de faire le même travail / d'obtenir les résultats escomptés.

Et les responsabilités / risques qui découlent du code sont sur les épaules des développeurs.

C'est petit mais je suppose qu'un exemple très utile du monde .NET ..

De nombreux développeurs .NET utilisent le BinaryReader - BinaryWriter intégré pour leur sérialisation des données pour des performances / contrôle du processus.

Il s'agit du code source CSharp de la classe BinaryWriter intégrée à FrameWork, l'une des méthodes d'écriture surchargées:

// Writes a boolean to this stream. A single byte is written to the stream
// with the value 0 representing false or the value 1 representing true.
// 
public virtual void Write(bool value) 
{
     //_buffer is a byte array which declared in ctor / init codes of the class
    _buffer = ((byte) (value? 1:0));

    //OutStream is the stream instance which BinaryWriter Writes the value(s) into it.
    OutStream.WriteByte(_buffer[0]);
}

Comme vous le voyez, cette méthode pourrait être écrite sans l'attribution supplémentaire à la variable _buffer:

public virtual void Write(bool value) 
{
    OutStream.WriteByte((byte) (value ? 1 : 0));
}

Sans attribution, nous pourrions gagner quelques millisecondes ... Ces quelques millisecondes peuvent accepter comme "presque rien" mais que se passe-t-il s'il y a plusieurs milliers d'écritures (c'est-à-dire dans un processus serveur)?

Supposons que "peu" soit 2 (millisecondes) et que les instances à plusieurs milliers ne soient que 2.000 .. Cela signifie 4 secondes de temps de processus en plus ... 4 secondes plus tard pour revenir ..

Si nous continuons à soumettre à partir de .NET et si vous pouvez vérifier les codes source de BCL - Bibliothèque de classes de base .NET - à partir de MSDN, vous pouvez voir beaucoup de pertes de performances du développeur décide.

N'importe quel point de la source BCL Il est normal que le développeur décide d'utiliser des boucles while () ou foreach () qui pourraient implémenter une boucle for () plus rapide dans leur code.

Ces petits gains nous donnent la performance totale ..

Et si nous revenons à la méthode BinaryWriter.Write () ..

En fait, l'affectation supplémentaire à une implémentation de _buffer n'est pas une faute de développeur.

Supposons que nous décidions de ne pas utiliser _buffer et que nous décidions d'implémenter la deuxième méthode .. Si nous essayons d'envoyer plusieurs milliers d'octets sur un câble (c'est-à-dire télécharger / télécharger des données BLOB ou CLOB) avec la deuxième méthode, cela peut échouer fréquemment car de connexion perdue. Parce que nous essayons d'envoyer toutes les données sans aucun contrôle ni mécanisme de contrôle. Lorsque la connexion est perdue, le serveur et le client ne savent jamais que les données envoyées sont terminées ou non.

Si le développeur décide de "rester en sécurité", cela signifie normalement que les coûts de performance dépendent des mécanismes "de rester en sécurité" mis en œuvre.

Mais si le développeur décide de "devenir risqué, gagner en performances", ce n'est pas non plus un défaut.

Et comme une petite note: les développeurs de bibliothèques commerciales essaient toujours de rester en sécurité car ils ne peuvent pas savoir où leur code va utiliser.

sihirbazzz
la source
4

Du point de vue des programmeurs, les frameworks ciblent rarement les performances comme la plus haute priorité. Si votre bibliothèque va être largement exploitée, les choses que les gens sont susceptibles d'apprécier le plus sont la facilité d'utilisation, la flexibilité et la fiabilité.

La performance est généralement valorisée dans les bibliothèques compétitives secondaires. "La bibliothèque X est meilleure parce qu'elle est plus rapide." Même alors très fréquemment, ces bibliothèques échangeront la solution la plus optimale contre une solution qui peut être largement exploitée.

En utilisant n'importe quel framework, vous prenez intrinsèquement le risque qu'une solution plus rapide existe. Je pourrais même aller jusqu'à dire qu'une solution plus rapide existe presque toujours.

Écrire quelque chose vous-même n'est pas une garantie de performance, mais si vous savez ce que vous faites et que vous avez un ensemble d'exigences assez limité, cela peut vous aider.

Un exemple pourrait être l'analyse JSON. Il existe une centaine de bibliothèques pour une variété de langages qui transformeront JSON en objet référencable et vice versa. Je connais une implémentation qui fait tout dans les registres CPU. C'est beaucoup plus rapide que tous les autres analyseurs, mais il est également très limité et cette limitation variera en fonction du processeur avec lequel vous travaillez.

La tâche de créer un analyseur JSON spécifique à un environnement hautement performant est-elle une bonne idée? Je tirerais parti d'une bibliothèque respectée 99 fois sur 100. Dans cette instance distincte, quelques cycles CPU supplémentaires multipliés par un million d'itérations feraient que le temps de développement en vaut la peine.

Steve Kallestad
la source