7 votes

Runtime.getRuntime().availableProcessors() renvoie 1 même si de nombreuses cœurs sont disponibles sur ECS AWS

Je lance une tâche via Docker sur ECS d'AWS. La tâche effectue des calculs qui sont liés au CPU, et que je voudrais exécuter en parallèle. Je démarre un pool de threads avec le nombre de threads spécifié dans Runtime.getRuntime().availableProcessors(), ce qui fonctionne bien en local sur mon PC. Pour une raison quelconque, sur AWS ECS, cela retourne toujours 1, même s'il y a plusieurs cœurs disponibles. Par conséquent, mes calculs s'exécutent séquentiellement et n'utilisent pas les multiples cœurs.

Par exemple, en ce moment, j'ai une tâche qui s'exécute sur une instance "t3.medium" qui devrait avoir 2 cœurs selon la documentation.

Lorsque j'exécute le code suivant :

System.out.println("Java rapporte " + 
    Runtime.getRuntime().availableProcessors() + " cœurs");

Ensuite, ce qui suit s'affiche dans les logs :

Java rapporte 1 cœur

Je ne spécifie pas le paramètre cpu dans la définition de tâche d'ECS. Je vois que dans la liste des tâches dans la console de gestion ECS, il y a une colonne "CPU" qui affiche 0 pour ma tâche. Je remarque également que dans la liste des instances (= VMs), il est indiqué "CPU disponible" comme 2048, ce qui a probablement quelque chose à voir avec le fait que la VM a 2 cœurs.

Je voudrais que mon programme Java voie tous les cœurs que la VM a à offrir. (Comme c'est normalement le cas lorsqu'un programme Java s'exécute sur un ordinateur sans Docker).

Comment puis-je faire cela ?

3voto

Adrian Smith Points 6087

Merci à @stdunbar dans les commentaires pour m'avoir orienté dans la bonne direction.

EDIT : Merci à @Imran dans les commentaires. Si vous lancez beaucoup de threads, ils seront absolument programmés sur plusieurs cœurs. Cette réponse concerne uniquement le fait d'obtenir Runtime.getRuntime().availableProcessors() pour retourner la bonne valeur. De nombreux "pools de threads" lancent autant de threads que cette méthode retourne : elle devrait retourner le nombre de cœurs disponibles.

Il semble y avoir deux solutions principales, aucune d'entre elles n'étant idéale :

  • Définissez le paramètre cpu dans la définition de la tâche. Par exemple, si vous avez 2 cœurs et que vous voulez les utiliser tous les deux, vous devez définir "cpu":2048 dans la définition de la tâche. Ce n'est pas très pratique pour deux raisons :

    • Si vous choisissez une instance plus grande, vous devez vous assurer de mettre à jour ce paramètre.

    • Si vous voulez exécuter simultanément deux tâches, dont chacune peut utiliser sporadiquement tous les cœurs pour des activités à court terme, AWS ne planifiera pas deux tâches sur un système de 2 cœurs avec "cpu":2048. Il dit que la VM est "pleine" du point de vue du CPU. Cela va à l'encontre de la philosophie du partage des ressources temporelles (Unix, etc.), où chaque tâche prend ce dont elle a besoin (par exemple, imaginez sur un PC de bureau, si vous exécutez Word et Excel sur un ordinateur double cœur, et si Windows ne vous permettait pas de démarrer d'autres tâches, au motif que Word pourrait avoir besoin de tout un cœur, et Excel pourrait aussi le faire, donc si un autre programme pourrait avoir besoin de tout le cœur en même temps, il n'y aurait pas assez de cœurs.)

  • Utilisez l'option JVM -XX:ActiveProcessorCount=xx dans JDK 10 et plus, comme décrit ici. Ce n'est pas pratique car :

    • Comme ci-dessus, vous devez changer la valeur si vous changez le type de votre instance.

J'ai écrit un long article de blog décrivant mes découvertes ici : https://www.databasesandlife.com/java-docker-aws-ecs-multicore/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X