web-dev-qa-db-fra.com

scala vs Java, performances et mémoire?)

J'ai hâte de me pencher sur Scala et j'ai une question de base à laquelle il semble que je ne trouve pas de réponse: en général, existe-t-il une différence de performances et d'utilisation de la mémoire entre Scala et Java?)?

155
JohnSmith

Scala facilite l'utilisation d'une énorme quantité de mémoire sans s'en rendre compte. Ceci est généralement très puissant, mais peut parfois être agaçant. Par exemple, supposons que vous disposiez d'un tableau de chaînes (appelé array) et d'une carte de ces chaînes en fichiers (appelé mapping). Supposons que vous souhaitiez obtenir tous les fichiers de la carte provenant de chaînes de longueur supérieure à deux. En Java, vous pourriez

int n = 0;
for (String s: array) {
  if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
  if (s.length <= 2) continue;
  bigEnough[n++] = map.get(s);
}

Ouf! Un dur travail. En Scala, le moyen le plus compact de faire la même chose est:

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)

Facile! Mais, à moins que vous ne connaissiez bien le fonctionnement des collections, il est possible que vous ne réalisiez pas que cette façon de procéder a créé un tableau intermédiaire supplémentaire (avec filter) et un objet supplémentaire pour chaque élément du tableau (avec mapping.get, ce qui retourne une option). Il crée également deux objets de fonction (un pour le filtre et un pour le flatMap), bien que ce soit rarement un problème majeur car les objets de fonction sont petits.

Donc, fondamentalement, l’utilisation de la mémoire est, à un niveau primitif, identique. Mais les bibliothèques de Scala disposent de nombreuses méthodes puissantes qui vous permettent de créer très facilement un grand nombre d'objets (généralement de courte durée). Le ramasse-miettes est généralement assez bon avec ce genre de poubelle, mais si vous entrez complètement inconscient de la mémoire utilisée, vous aurez probablement des ennuis plus tôt dans Scala que Java.

Notez que le code du jeu Scala du langage de langage informatique est écrit dans un style assez semblable à Java afin d'obtenir des performances similaires à celles de Java et a donc une utilisation de la mémoire semblable à celle de Java. Vous pouvez le faire dans Scala: si vous écrivez votre code pour ressembler à du code Java hautes performances, il s'agira d'un code Scala hautes performances. (Vous pouvez pouvoir l'écrire dans un style plus idiomatique Scala tout en obtenant de bonnes performances, mais cela dépend des spécificités.)

Je devrais ajouter que, par temps de programmation, mon code Scala est généralement plus rapide que mon code Java car, dans Scala Je peux obtenir les parties fastidieuses, non critiques en termes de performances, avec moins d'effort et consacrer plus d'attention à l'optimisation des algorithmes et du code pour les parties critiques en termes de performances.

252
Rex Kerr

Je suis un nouvel utilisateur, je ne peux donc pas ajouter de commentaire à la réponse de Rex Kerr ci-dessus (autoriser les nouveaux utilisateurs à "répondre" mais pas à "commenter" est une règle très étrange).

Je me suis inscrit simplement pour répondre au message "ouf, Java est si prolixe et si difficile") en insinuant la réponse populaire de Rex ci-dessus. Bien que vous puissiez bien sûr écrire plus concis Scala, l'exemple Java donné est clairement gonflé. La plupart des développeurs Java codifierait quelque chose comme ceci:

List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
  if(s.length() > 2 && mapping.get(s) != null) {
    bigEnough.add(mapping.get(s));
  }
}

Et bien sûr, si nous prétendons qu'Eclipse ne fait pas la majeure partie de la frappe et que chaque caractère enregistré fait de vous un meilleur programmeur, vous pouvez coder ceci:

List b=new ArrayList();
for(String s:array)
  if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));

Maintenant, non seulement j'ai économisé le temps qu'il m'a fallu pour taper des noms de variable complets et des accolades (cela me libère de dépenser 5 secondes de plus pour réfléchir à de profondes pensées algorithmiques), mais je peux aussi entrer mon code dans des concours d'obfuscation et potentiellement gagner de l'argent supplémentaire pour les vacances.

100
Not Sleeping

Ecrivez votre Scala comme Java, et vous pouvez vous attendre à ce que du bytecode presque identique soit émis - avec des métriques presque identiques.

Ecrivez-le plus "idiomatiquement", avec des objets immuables et des fonctions d'ordre supérieur, et ce sera un peu plus lent et un peu plus grand. La seule exception à cette règle empirique concerne les objets génériques dans lesquels les paramètres de type utilisent le paramètre @specialised annotation, cela créera un bytecode encore plus volumineux qui peut dépasser les performances de Java en évitant la boxing/unboxing.

Il convient également de mentionner le fait que plus de mémoire/moins de vitesse est un compromis inévitable lors de l'écriture de code pouvant être exécuté en parallèle. Idiomatic Scala est de nature beaucoup plus déclarative que le code typique Java, et ne contient souvent que 4 caractères (.par) loin d’être complètement parallèle.

Donc si

  • Le code Scala prend 1,25 fois plus longtemps que Java dans un seul thread
  • Il peut être facilement divisé en 4 cœurs (maintenant commun même dans les ordinateurs portables)
  • pour une durée d’exécution parallèle de (1,24/4 =) 0,3125x la valeur Java initiale

Diriez-vous alors que le code Scala est maintenant 25% plus lent, ou 3 fois plus rapide?)

La réponse correcte dépend de la façon dont vous définissez la "performance" :)

64
Kevin Wright

Jeu de tests de langage informatique:

test de vitesse Java/scala 1.71/2.25

Test de la mémoire Java/scala 66.55/80.81

Ainsi, ces tests indiquent que Java est 24% plus rapide et scala utilise 21% de mémoire supplémentaire.

Dans l'ensemble, ce n'est pas grave et cela ne devrait pas compter dans les applications du monde réel, où la plupart du temps est consommé par la base de données et le réseau.

Résultat final: Si Scala vous rend plus productifs, ainsi que votre équipe (et les personnes prenant en charge le projet à votre départ), alors vous devriez y aller.

31
Peter Knego

D'autres ont répondu à cette question en ce qui concerne les boucles serrées bien qu'il semble y avoir une différence de performance évidente entre les exemples de Rex Kerr que j'ai commentés.

Cette réponse est vraiment destinée aux personnes susceptibles d’examiner le besoin d’optimisation en boucle étroite en tant que défaut de conception.

Je suis relativement nouveau sur Scala (environ un an)), mais l’ambiance actuelle est que cela vous permet de différer de nombreux aspects de conception, mise en œuvre et exécution relativement facilement (avec suffisamment de lecture en arrière-plan et d'expérimentation :)

Caractéristiques de conception différées:

Fonctions d'implémentation différée:

Fonctionnalités d'exécution différée: (désolé, pas de liens)

  • Valeurs paresseuses fil-safe
  • Pass-by-name
  • Trucs monadiques

Pour moi, ces fonctionnalités nous aident à nous frayer un chemin vers des applications rapides et compactes.


Les exemples de Rex Kerr diffèrent quant aux aspects d'exécution différés. Dans l'exemple Java, l'allocation de mémoire est différée jusqu'à ce que sa taille soit calculée là où l'exemple Scala diffère la recherche de mappage. Pour moi, elles semblent complètement différentes algorithmes.

Voici ce que je pense est plus d'un équivalent de pommes à pommes pour son exemple Java:

val bigEnough = array.collect({
    case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})

Pas de collections intermédiaires, pas de Option instances, etc. Cela préserve également le type de collection, donc le type de bigEnough est Array[File] _ _ Array L'implémentation de collect de _ fera probablement quelque chose dans le sens de ce que fait M. Kerr Java).

Les fonctionnalités de conception différées que j'ai énumérées ci-dessus permettraient également aux développeurs d'API de collection de Scala d'implémenter cette implémentation de collecte rapide spécifique à Array dans les versions ultérieures sans casser l'API. C’est ce à quoi je fais allusion lorsque nous empruntons le chemin de la vitesse.

Aussi:

val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)

La méthode withFilter que j'ai utilisée ici au lieu de filter résout le problème de la collection intermédiaire, mais il y a toujours le problème de l'instance Option.


Un exemple de vitesse d'exécution simple dans Scala est avec la journalisation.

Dans Java nous pourrions écrire quelque chose comme:

if (logger.isDebugEnabled())
    logger.debug("trace");

En Scala, c'est juste:

logger.debug("trace")

parce que le paramètre de message à déboguer Scala a le type "=> String "que je considère comme une fonction sans paramètre qui s’exécute lors de son évaluation, mais que la documentation appelle mot de passe.

EDIT {Les fonctions dans Scala sont des objets, donc il y a un objet supplémentaire ici. Pour mon travail, le poids d'un objet trivial vaut la peine d'éliminer la possibilité qu'un message de journal soit évalué inutilement.}

Cela ne rend pas le code plus rapide, mais le rend plus susceptible d'être plus rapide et nous sommes moins susceptibles d'avoir l'expérience de nettoyer et de nettoyer le code des autres en masse.

Pour moi, c'est un thème récurrent au sein de Scala.


Le code dur ne parvient pas à saisir pourquoi Scala est plus rapide bien que cela laisse deviner un peu.

Je pense que c'est une combinaison de la réutilisation de code et du plafond de qualité du code dans Scala.

En Java, un code génial est souvent contraint de devenir un gâchis incompréhensible et n'est donc pas vraiment viable dans les API de qualité de production, car la plupart des programmeurs ne seraient pas en mesure de l'utiliser.

J'espère beaucoup que Scala pourrait permettre aux einsteins parmi nous d'implémenter des API beaucoup plus compétentes, potentiellement exprimées par le biais de DSL. Les API de base dans Scala sont déjà bien loin le long de ce chemin.

20
Seth

@ higherkinded La présentation sur le sujet - Considérations sur les performances Scala qui effectue certaines comparaisons Java/Scala.

Outils:

Super blogpost:

11
oluies

Java et Scala sont tous les deux compilés en JCC, la différence n’est donc pas si grande. La meilleure comparaison que vous pouvez obtenir est probablement le jeu de tests de langage informatique , qui dit essentiellement que Java et Scala ont tous deux la même utilisation de la mémoire. Scala est seulement légèrement plus lent que Java sur certains des points de repère répertoriés, mais cela peut simplement être dû au fait que la mise en œuvre des programmes est différente.

Vraiment cependant, ils sont tous deux si proches qu'il ne faut pas s'en inquiéter. L'augmentation de la productivité que vous obtenez en utilisant un langage plus expressif tel que Scala vaut bien plus que de nuire à la performance, voire à la minimiser.

10
ryeguy

L'exemple Java n'est pas vraiment un idiome pour les programmes d'application typiques. Ce code optimisé pourrait être trouvé dans une méthode de bibliothèque système. Mais il utiliserait alors un tableau du type approprié, c'est-à-dire File [] et ne jetterait pas une exception IndexOutOfBoundsException. (Différentes conditions de filtrage pour compter et ajouter). Ma version serait (toujours)! avec des accolades car je n'aime pas passer une heure à la recherche d'un bogue introduit en enregistrant les 2 secondes appuyer sur une seule touche dans Eclipse):

List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
  if(s.length() > 2) {
    File file = mapping.get(s);
    if (file != null) {
      bigEnough.add(file);
    }
  }
}

Mais je pourrais vous apporter beaucoup d'autres exemples de code laids Java de mon projet actuel). J'ai essayé d'éviter le style de codage de copie et de modification courant en prenant en compte les structures et les comportements communs.

Dans ma classe de base DAO abstraite, j'ai une classe interne abstraite pour le mécanisme de mise en cache commun. Pour chaque type d'objet de modèle concret, il existe une sous-classe de la classe de base DAO abstraite, dans laquelle la classe interne est sous-classée afin de fournir une implémentation de la méthode qui crée l'objet métier lorsqu'il est chargé à partir de la base de données. (Nous ne pouvons pas utiliser un outil ORM car nous accédons à un autre système via une API propriétaire.)

Ce code de sous-classement et d'instanciation n'est pas du tout clair dans Java et serait très lisible dans Scala.

4
MickH