@@ -473,7 +473,15 @@ func (ex *RunExecutor) execJob(ctx context.Context, jobLogFile io.Writer) error
473473 nodeRank := ex .jobSpec .JobNum
474474 nodesNum := ex .jobSpec .JobsPerReplica
475475 gpusPerNodeNum := ex .clusterInfo .GPUSPerJob
476- gpusNum := nodesNum * gpusPerNodeNum
476+ gpusNum := 0
477+ if len (ex .clusterInfo .GPUSPerNode ) > 0 {
478+ for _ , n := range ex .clusterInfo .GPUSPerNode {
479+ gpusNum += n
480+ }
481+ } else {
482+ // Old servers omit gpus_per_node; fall back to homogeneous math.
483+ gpusNum = nodesNum * gpusPerNodeNum
484+ }
477485
478486 mpiHostfilePath := filepath .Join (ex .dstackDir , "mpi/hostfile" )
479487
@@ -544,7 +552,15 @@ func (ex *RunExecutor) execJob(ctx context.Context, jobLogFile io.Writer) error
544552 log .Warning (ctx , "failed to include dstack_profile" , "path" , profilePath , "err" , err )
545553 }
546554
547- if err := writeMpiHostfile (ctx , ex .clusterInfo .JobIPs , gpusPerNodeNum , mpiHostfilePath ); err != nil {
555+ slots := ex .clusterInfo .GPUSPerNode
556+ if len (slots ) == 0 {
557+ // Old servers omit gpus_per_node; fall back to homogeneous per-node GPU count.
558+ slots = make ([]int , len (ex .clusterInfo .JobIPs ))
559+ for i := range slots {
560+ slots [i ] = gpusPerNodeNum
561+ }
562+ }
563+ if err := writeMpiHostfile (ctx , ex .clusterInfo .JobIPs , slots , mpiHostfilePath ); err != nil {
548564 return fmt .Errorf ("write MPI hostfile: %w" , err )
549565 }
550566
@@ -759,7 +775,7 @@ func prepareUserSshDir(user *linuxuser.User) (string, error) {
759775 return sshDir , nil
760776}
761777
762- func writeMpiHostfile (ctx context.Context , ips []string , gpusPerNode int , path string ) error {
778+ func writeMpiHostfile (ctx context.Context , ips []string , slots [] int , path string ) error {
763779 if err := os .MkdirAll (filepath .Dir (path ), 0o755 ); err != nil {
764780 return fmt .Errorf ("create MPI hostfile directory: %w" , err )
765781 }
@@ -775,16 +791,21 @@ func writeMpiHostfile(ctx context.Context, ips []string, gpusPerNode int, path s
775791 }
776792 }
777793 if len (nonEmptyIps ) == len (ips ) {
778- var template string
779- if gpusPerNode == 0 {
780- // CPU node: the number of slots defaults to the number of processor cores on that host
781- // See: https://docs.open-mpi.org/en/main/launching-apps/scheduling.html#calculating-the-number-of-slots
782- template = "%s\n "
783- } else {
784- template = fmt .Sprintf ("%%s slots=%d\n " , gpusPerNode )
794+ if len (slots ) != len (ips ) {
795+ return fmt .Errorf (
796+ "gpus_per_node length %d != job_ips length %d" ,
797+ len (slots ), len (ips ),
798+ )
785799 }
786- for _ , ip := range nonEmptyIps {
787- if _ , err = fmt .Fprintf (file , template , ip ); err != nil {
800+ for i , ip := range nonEmptyIps {
801+ if slots [i ] == 0 {
802+ // CPU node: the number of slots defaults to the number of processor cores on that host
803+ // See: https://docs.open-mpi.org/en/main/launching-apps/scheduling.html#calculating-the-number-of-slots
804+ _ , err = fmt .Fprintf (file , "%s\n " , ip )
805+ } else {
806+ _ , err = fmt .Fprintf (file , "%s slots=%d\n " , ip , slots [i ])
807+ }
808+ if err != nil {
788809 return fmt .Errorf ("write MPI hostfile line: %w" , err )
789810 }
790811 }
0 commit comments