Spenserrrr commented on code in PR #59246:
URL: https://github.com/apache/spark/pull/59246#discussion_r4252025175


##########
python/pyspark/worker.py:
##########
@@ -128,21 +128,88 @@
 )
 
 
-def report_metrics(
-    outfile, boot, init, finish, execution_duration_ms, memory_bytes_spilled, 
disk_bytes_spilled
-):
+class _WorkerTimer:
+    """Measure one named duration and add each interval to the collector's 
task total.
+
+    The collector owns the duration dictionary. This scope keeps a reference 
to it and a
+    start time for its current with block. Reuse the scope across sequential 
batches.
+    """
+
+    def __init__(self, shared_duration_totals_ns: dict[str, int], metric_name: 
str) -> None:
+        """Bind one metric name to the collector's shared duration totals."""
+        # Keep the collector's dictionary by reference so updates reach its 
task totals.
+        self._shared_duration_totals_ns = shared_duration_totals_ns
+        self._metric_name = metric_name
+        self._block_start_ns: Optional[int] = None
+
+    def __enter__(self) -> None:
+        """Start the clock when Python enters the with block."""
+        # Re-entering this instance would overwrite an unfinished 
measurement's start time.
+        if self._block_start_ns is not None:
+            raise RuntimeError("Worker timer is already running")
+        self._block_start_ns = time.perf_counter_ns()
+
+    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
+        """Accumulate elapsed time when Python leaves the block, including on 
an exception."""
+        assert self._block_start_ns is not None
+        elapsed_ns = time.perf_counter_ns() - self._block_start_ns
+        self._block_start_ns = None
+        self._shared_duration_totals_ns[self._metric_name] += elapsed_ns
+        # Python supplies the exception arguments; returning None lets the 
error propagate.
+
+
+class WorkerMetrics:
+    """Collect values and duration totals for one worker task.
+
+    set/add store values in their reporting units: counts, byte counts, or 
epoch milliseconds.
+    measure creates timer scopes that accumulate nanoseconds in a separate 
shared dictionary.
+    to_report combines both dictionaries, converting only durations to 
milliseconds.
+    """
+
+    def __init__(self) -> None:
+        """Create fresh task state so a reused worker does not retain the 
previous totals."""
+        # Counters, spill byte counts, and epoch timestamps need no conversion 
at report time.
+        self._values_in_report_units: dict[str, int] = {}
+        # These totals belong to the collector; timer scopes share and update 
them.
+        self._duration_totals_ns: dict[str, int] = {}
+
+    def set(self, name: str, value: int) -> None:
+        """Store a value in its reporting unit, replacing any earlier value.
+
+        Used for initial counter values, epoch timestamps, and final spill 
byte counts.
+        Elapsed durations are accumulated through measure instead.
+        """
+        self._values_in_report_units[name] = value
+
+    def add(self, name: str, value: int = 1) -> None:

Review Comment:
   The collector API now uses `increment` and `to_dict`, and per-batch `with` 
timer scopes were removed from the Arrow hot loop. Phase durations accumulate 
locally and flush once per task or stream. The performance study measured 
runtime overhead, but did not directly quantify timing error from repeated 
clock samples; I will treat that accuracy question as follow-up work. Resolving 
this thread with that limitation explicit.



##########
python/pyspark/worker.py:
##########
@@ -1808,6 +1875,7 @@ def read_udfs(pickleSer, udf_info_list, eval_type, 
runner_conf, eval_conf):
             for udf_index, udf_info in enumerate(udf_info_list)
         ]
         handler = handler_cls(udfs=udfs, runner_conf=runner_conf, 
eval_conf=eval_conf)
+        handler.set_worker_metrics(metrics)

Review Comment:
   Agreed. `read_udfs` no longer receives metrics or sets them on a handler. 
The shared collector is reset at worker task entry, so this older wiring 
concern is addressed. Resolving this thread.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to