@@ -462,6 +462,60 @@ print.Future <- function(x, ...) {
462462 t1 <- result [[" finished" ]]
463463 cat(sprintf(" Duration: %s (started %s)\n " , format(t1 - t0 ), t0 ))
464464 cat(sprintf(" Worker process: %s\n " , result [[" session_uuid" ]]))
465+
466+ # # Efficiency breakdown (when journaling is enabled)
467+ if (inherits(future [[" .journal" ]], " FutureJournal" )) {
468+ j <- tryCatch(journal(future ), error = function (e ) NULL )
469+ if (! is.null(j )) {
470+ # # Only top-level events; sub-events (parent != NA) overlap in
471+ # # wall-clock with their parent and would double-count.
472+ j_top <- j [is.na(j [[" parent" ]]), , drop = FALSE ]
473+ j_top [[" stop" ]] <- j_top [[" start" ]] + j_top [[" duration" ]]
474+ create_idx <- which(j_top [[" event" ]] == " create" )
475+ gather_idx <- which(j_top [[" event" ]] == " gather" )
476+ over_idx <- which(j_top [[" category" ]] == " overhead" )
477+ eval_idx <- which(j_top [[" category" ]] == " evaluation" )
478+ if (length(create_idx ) > 0L && length(gather_idx ) > 0L &&
479+ length(eval_idx ) > 0L ) {
480+ g <- gather_idx [length(gather_idx )]
481+ walltime <- (j_top [[" start" ]][g ] + j_top [[" duration" ]][g ]) -
482+ j_top [[" start" ]][create_idx [1 ]]
483+ eval_dur <- sum(j_top [[" duration" ]][eval_idx ])
484+ over_dur_raw <- sum(j_top [[" duration" ]][over_idx ])
485+ # # For backends like 'sequential' where 'launch' temporally
486+ # # wraps 'evaluate', subtract that overlap so it isn't
487+ # # double-counted as both overhead and evaluation.
488+ overlap_secs <- 0
489+ for (i in over_idx ) for (k in eval_idx ) {
490+ ov <- as.numeric(min(j_top [[" stop" ]][i ], j_top [[" stop" ]][k ]) -
491+ max(j_top [[" start" ]][i ], j_top [[" start" ]][k ]),
492+ units = " secs" )
493+ if (ov > 0 ) overlap_secs <- overlap_secs + ov
494+ }
495+ over_secs <- max(as.numeric(over_dur_raw , units = " secs" ) -
496+ overlap_secs , 0 )
497+ eval_secs <- as.numeric(eval_dur , units = " secs" )
498+ # # "Active" round-trip excludes idle time (e.g. between
499+ # # future() and value()) and master-blocked-on-worker time
500+ # # that runs in parallel with evaluation.
501+ active_secs <- over_secs + eval_secs
502+ wall_secs <- as.numeric(walltime , units = " secs" )
503+ idle_secs <- max(wall_secs - active_secs , 0 )
504+ overhead <- as.difftime(over_secs , units = " secs" )
505+ active <- as.difftime(active_secs , units = " secs" )
506+ idle <- as.difftime(idle_secs , units = " secs" )
507+ cat(" Efficiency:\n " )
508+ cat(sprintf(" Round-trip: %s (active = overhead + evaluation; wall-clock %s, idle %s)\n " ,
509+ format(active ), format(walltime ), format(idle )))
510+ if (active_secs > 0 ) {
511+ cat(sprintf(" Evaluation: %s (%.1f%%) [worker]\n " ,
512+ format(eval_dur ), 100 * eval_secs / active_secs ))
513+ cat(sprintf(" Overhead: %s (%.1f%%) [orchestration]\n " ,
514+ format(overhead ), 100 * over_secs / active_secs ))
515+ }
516+ }
517+ }
518+ }
465519 } else {
466520 cat(" Value: <not collected>\n " )
467521 cat(" Conditions captured: <none>\n " )
0 commit comments