Skip to content

Commit 2b3fae8

Browse files
Merge branch 'main' into mlir/pr7-masked-contains
2 parents 7249944 + 315a84f commit 2b3fae8

8 files changed

Lines changed: 562 additions & 474 deletions

File tree

‎python/cudf_polars/cudf_polars/dsl/utils/io.py‎

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515

1616
from cudf_polars.dsl.tracing import nvtx_annotate_cudf_polars
1717
from cudf_polars.dsl.traversal import traversal
18-
from cudf_polars.streaming.io import Scan, StreamingScan
18+
from cudf_polars.streaming.io import (
19+
ParquetSourceInfo,
20+
Scan,
21+
StreamingScan,
22+
)
1923

2024
if TYPE_CHECKING:
2125
from cudf_polars.dsl.ir import IR
@@ -181,14 +185,12 @@ def prefetch_parquet_file_metadata_for_ir(
181185
-------
182186
A dictionary mapping each individual path to its cached parquet metadata.
183187
"""
184-
from cudf_polars.streaming.io import ParquetSourceInfo, StreamingScan
185-
186188
all_paths: set[str] = set()
187189

188190
for node in traversal([root]):
189191
if isinstance(node, StreamingScan) and node.base_scan.typ == "parquet":
190-
for scan in node.scans:
191-
for path in scan.paths:
192+
for task in node.tasks:
193+
for path in task.paths:
192194
all_paths.add(path)
193195
elif isinstance(node, Scan) and node.typ == "parquet": # pragma: no cover
194196
raise RuntimeError("Unexpected parquet 'Scan' node in lowered IR graph.")
@@ -241,7 +243,7 @@ def attach_cached_parquet_metadata(
241243
cached_parquet_info_map: dict[str, CachedParquetInfo],
242244
) -> None:
243245
"""
244-
Attach prefetched metadata to scan nodes.
246+
Attach prefetched metadata to parquet scan tasks.
245247
246248
This is an optimization only and does not affect IR identity.
247249
@@ -254,10 +256,15 @@ def attach_cached_parquet_metadata(
254256
"""
255257
for node in traversal([root]):
256258
if isinstance(node, StreamingScan) and node.base_scan.typ == "parquet":
257-
for scan in node.scans:
258-
if not all(path in cached_parquet_info_map for path in scan.paths):
259-
continue
260-
cached = [cached_parquet_info_map[path] for path in scan.paths]
261-
Scan._validate_cached_parquet_info(scan.paths, cached)
262-
scan.cached_parquet_info = cached
263-
scan._non_child_args = (*scan._non_child_args[:-1], cached)
259+
base_scan = node.base_scan
260+
task_paths = {path for task in node.tasks for path in task.paths}
261+
cached_paths = [
262+
path
263+
for path in base_scan.paths
264+
if path in task_paths and path in cached_parquet_info_map
265+
]
266+
cached = [cached_parquet_info_map[path] for path in cached_paths]
267+
if not cached:
268+
continue
269+
Scan._validate_cached_parquet_info(cached_paths, cached)
270+
base_scan.cached_parquet_info = cached

‎python/cudf_polars/cudf_polars/streaming/actor_graph/io.py‎

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
IOPartitionPlan,
6868
PartitionInfo,
6969
)
70-
from cudf_polars.streaming.io import FusedScan, SplitScan
70+
from cudf_polars.streaming.io import ScanTask
7171
from cudf_polars.utils.config import MaxConcurrentIOTasks
7272

7373

@@ -545,7 +545,7 @@ def _(
545545

546546
async def read_chunk(
547547
context: Context,
548-
scan: IR,
548+
task: IR,
549549
seq_num: int,
550550
ch_out: Channel[TableChunk],
551551
ir_context: IRExecutionContext,
@@ -559,8 +559,8 @@ async def read_chunk(
559559
----------
560560
context
561561
The rapidsmpf context.
562-
scan
563-
The Scan or DataFrameScan node.
562+
task
563+
The scan task to evaluate.
564564
seq_num
565565
The sequence number.
566566
ch_out
@@ -575,7 +575,7 @@ async def read_chunk(
575575
"""
576576
reservation_bytes = (
577577
estimated_chunk_bytes
578-
if isinstance(scan, DataFrameScan)
578+
if isinstance(task, DataFrameScan)
579579
else 2 * estimated_chunk_bytes
580580
)
581581
start = time.monotonic_ns()
@@ -587,8 +587,8 @@ async def read_chunk(
587587
admitted = time.monotonic_ns()
588588
with opaque_memory_usage(reservation):
589589
df = await ir_context.to_thread(
590-
scan.do_evaluate,
591-
*scan._non_child_args,
590+
task.do_evaluate,
591+
*task._non_child_args,
592592
context=ir_context,
593593
)
594594
chunk = TableChunk.from_pylibcudf_table(
@@ -604,8 +604,8 @@ async def read_chunk(
604604
start=start,
605605
admitted=admitted,
606606
stop=stop,
607-
ir_id=scan.get_stable_id(),
608-
ir_type=type(scan).__name__,
607+
ir_id=task.get_stable_id(),
608+
ir_type=type(task).__name__,
609609
sequence_number=seq_num,
610610
estimated_output_bytes=estimated_chunk_bytes,
611611
reservation_bytes=reservation_bytes,
@@ -642,7 +642,7 @@ async def scan_node(
642642
Estimated retained output size of each chunk in bytes. Used to estimate
643643
peak memory for admission before launching each read.
644644
"""
645-
scans: Sequence[SplitScan] | Sequence[FusedScan] = ir.scans
645+
tasks: Sequence[ScanTask] = ir.tasks
646646

647647
async with shutdown_on_error(
648648
context, ch_out, trace_ir=ir, ir_context=ir_context
@@ -653,21 +653,21 @@ async def scan_node(
653653
await send_metadata(
654654
ch_out,
655655
context,
656-
ChannelMetadata(local_count=len(scans)),
656+
ChannelMetadata(local_count=len(tasks)),
657657
)
658658

659659
# If there is nothing to scan, drain the channel and return
660-
if len(scans) == 0:
660+
if len(tasks) == 0:
661661
await ch_out.drain(context)
662662
return
663663

664-
# If there is only one scan or one producer, we can
664+
# If there is only one task or one producer, we can
665665
# skip the lineariser and read the chunks directly
666-
if len(scans) == 1 or num_producers == 1:
667-
for seq_num, scan in enumerate(scans):
666+
if len(tasks) == 1 or num_producers == 1:
667+
for seq_num, task in enumerate(tasks):
668668
await read_chunk(
669669
context,
670-
scan,
670+
task,
671671
seq_num,
672672
ch_out,
673673
ir_context,
@@ -678,24 +678,23 @@ async def scan_node(
678678
return
679679

680680
# Use Lineariser to ensure ordered delivery
681-
num_producers = min(num_producers, len(scans))
681+
num_producers = min(num_producers, len(tasks))
682682
lineariser = Lineariser(context, ch_out, num_producers)
683683

684684
# Assign tasks to producers using round-robin
685-
producer_tasks: list[list[tuple[int, SplitScan | FusedScan]]] = [
685+
producer_tasks: list[list[tuple[int, ScanTask]]] = [
686686
[] for _ in range(num_producers)
687687
]
688-
for task_idx, scan in enumerate(scans):
688+
for task_idx, task in enumerate(tasks):
689689
producer_id = task_idx % num_producers
690-
# mypy resolves __iter__ on union-of-sequences to the common base (IR)
691-
producer_tasks[producer_id].append((task_idx, scan)) # type: ignore[arg-type]
690+
producer_tasks[producer_id].append((task_idx, task))
692691

693692
async def _producer(producer_id: int) -> None:
694-
for task_idx, scan in producer_tasks[producer_id]:
693+
for task_idx, task in producer_tasks[producer_id]:
695694
ch_out = await lineariser.acquire(producer_id)
696695
await read_chunk(
697696
context,
698-
scan,
697+
task,
699698
task_idx,
700699
ch_out,
701700
ir_context,

‎python/cudf_polars/cudf_polars/streaming/explain.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ def _(ir: Scan) -> dict[str, Serializable]:
599599
def _(ir: StreamingScan) -> dict[str, Serializable]:
600600
return {
601601
"typ": ir.base_scan.typ,
602-
"scan_count": len(ir.scans),
602+
"task_count": len(ir.tasks),
603603
"prefix": os.path.commonprefix(ir.base_scan.paths),
604604
"predicate": (
605605
_serialize_expr(ir.base_scan.predicate) if ir.base_scan.predicate else None

0 commit comments

Comments
 (0)