Skip to content

Use owning Arrow types in C++ to expose data to Python - #18402

Merged
rapids-bot[bot] merged 46 commits into
NVIDIA:branch-25.06from
vyasr:feat/arrow_data_structures_python
Apr 16, 2025
Merged

Use owning Arrow types in C++ to expose data to Python#18402
rapids-bot[bot] merged 46 commits into
NVIDIA:branch-25.06from
vyasr:feat/arrow_data_structures_python

Conversation

@vyasr

@vyasr vyasr commented Apr 1, 2025

Copy link
Copy Markdown
Contributor

Description

This PR leverages #18084 to rework the Python layer of Arrow interchange. With this change, we can now expose the Arrow capsule interfaces for pylibcudf Columns and Tables. This PR also paves the way for exposing the device capsules, which will allow us to provide zero-copy Arrow views into pylibcudf objects.

To get everything working, this PR also makes some ancillary changes:

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@vyasr vyasr added libcudf Affects libcudf (C++/CUDA) code. Python Affects Python cuDF API. improvement Improvement / enhancement to an existing function non-breaking Non-breaking change pylibcudf Issues specific to the pylibcudf package labels Apr 1, 2025
@vyasr vyasr self-assigned this Apr 1, 2025
@vyasr
vyasr requested a review from a team as a code owner April 1, 2025 18:56

@bdice bdice left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packaging and C++ look good. I skimmed Python/Cython changes too.

@shrshi shrshi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of nits in the C++ code, but LGTM otherwise!

Comment thread cpp/src/interop/to_arrow_device.cu
Comment thread cpp/src/interop/to_arrow_schema.cpp Outdated

@Matt711 Matt711 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just non-blocking suggestions, thanks!

@@ -0,0 +1,9 @@
# Copyright (c) 2025, NVIDIA CORPORATION.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think were missing type stubs _interop_helpers.pyi. Is that intentional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because this file pretty much exclusively exposes pure Cython functions. There is nothing visible to Python except the ColumnMetadata type, but that is (for the moment) primarily publicly exposed via the interop module that already has the type stubs for that.

Comment thread python/pylibcudf/pylibcudf/column.pyx Outdated
Comment thread python/pylibcudf/pylibcudf/interop.pyx Outdated
Comment thread python/pylibcudf/pylibcudf/interop.pyx Outdated
Comment thread python/pylibcudf/pylibcudf/libcudf/interop.pxd
Comment thread python/pylibcudf/pylibcudf/column.pyx Outdated
Comment thread cpp/src/interop/from_arrow_device.cu Outdated
NANOARROW_RETURN_NOT_OK(set_contents(child_contents, tmp->children[i]));
} else {
NANOARROW_RETURN_NOT_OK(cudf::type_dispatcher(
child->type(), dispatch_to_arrow_device{}, std::move(*child), stream, mr, child_ptr));

@kingcrimsontianyu kingcrimsontianyu Apr 14, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit paranoid on this: Do we have a guarantee that the initialization param_2 = std::move(*child) happens before param_0 = child->type()? If not, we may rely on the compiler's unspecified behavior for correctness.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cudf::type_dispatcher passes std::move(*child) with a forwarding reference, so the move has not happened yet when the function is entered, whereas param_0 = child->type() is sequenced before the function is entered. So perhaps it is fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we are safe because while we may not be guaranteed anything about the order in which the two parameters are evaluated, we are guaranteed that all of the parameters are evaluated before the function call begins. std::move doesn't actually do anything, it's just a cast to an rvalue that then allows the object to be modified. Therefore, even if we pass an rvalue ref to *child into the type_dispatcher (which forwards it along to the dispatch_to_arrow_device functor), we are guaranteed that param1 = child->type() is evaluated before *child is actually modified in any way that could evaluate it.

Comment thread cpp/src/interop/to_arrow_device.cu
Comment thread cpp/src/interop/to_arrow_device.cu
Comment thread cpp/src/interop/to_arrow_device.cu

@kingcrimsontianyu kingcrimsontianyu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C++ part looks good to me. Left one small suggestion, and one small question to confirm.

@vyasr

vyasr commented Apr 16, 2025

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit 9a2ccdf into NVIDIA:branch-25.06 Apr 16, 2025
@vyasr
vyasr deleted the feat/arrow_data_structures_python branch April 16, 2025 00:58
@vyasr

vyasr commented Apr 24, 2025

Copy link
Copy Markdown
Contributor Author

I forgot to post these benchmarks earlier, so posting for posterity (tl;dr this PR did not affect performance):

Before
--------------------------------------------------------------------------------------------------------------------------------------------------------- benchmark: 21 tests --[0/0]
  ---------------------------------------------------------------------------------------------------------------------------------------------------
  Name (time in us)                                                                                                                                        Min                    Max
                   Mean              StdDev                 Median                 IQR            Outliers          OPS            Rounds  Iterations
  -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  ---------------------------------------------------------------------------------------------------------------------------------------------------
  bench_to_arrow[-alt0-series_dtype_int_rows_100-series_dtype_int_nulls_false_rows_100]                                                                75.3720 (1.0)         113.1450 (
  1.0)          78.8339 (1.00)       2.6197 (1.07)         77.7130 (1.00)       3.5705 (4.67)       280;38  12,684.8998 (1.00)       2656           1
  bench_to_arrow[index_dtype_int_nulls_false-index_dtype_int_nulls_false_rows_100]                                                                     75.6590 (1.00)        148.6490 (
  1.31)         78.5329 (1.0)        4.4337 (1.81)         77.6930 (1.0)        0.7643 (1.0)       368;587  12,733.5156 (1.0)        7985           1
  bench_to_arrow[-alt0-series_dtype_int_rows_100-series_dtype_int_nulls_true_rows_100]                                                                 85.9940 (1.14)        568.3450 (
  5.02)         89.0394 (1.13)       7.5104 (3.07)         87.9520 (1.13)       1.2700 (1.66)       90;977  11,230.9841 (0.88)       4916           1
  bench_to_arrow[-alt0-series_dtype_int_rows_10000-series_dtype_int_nulls_false_rows_10000]                                                            94.3040 (1.25)        149.5130 (
  1.32)         97.2055 (1.24)       2.4453 (1.0)          96.8500 (1.25)       0.9970 (1.30)      225;271  10,287.4842 (0.81)       5199           1
  bench_to_arrow[index_dtype_int_nulls_false-index_dtype_int_nulls_false_rows_10000]                                                                   94.5990 (1.26)      1,594.6400 (
  14.09)        98.7860 (1.26)      18.6857 (7.64)         97.3675 (1.25)       1.0260 (1.34)      155;678  10,122.8904 (0.79)       7164           1
  bench_to_arrow[-alt0-series_dtype_int_rows_10000-series_dtype_int_nulls_true_rows_10000]                                                            104.6950 (1.39)        169.0670 (
  1.49)        107.9656 (1.37)       4.6451 (1.90)        107.0390 (1.38)       0.9220 (1.21)      177;278   9,262.2064 (0.73)       3341           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_100-dataframe_dtype_int_nulls_false_cols_1_rows_100]                430.8069 (5.72)        800.5180 (
  7.08)        439.0585 (5.59)      14.3906 (5.88)        436.0130 (5.61)       3.2210 (4.21)       72;160   2,277.6010 (0.18)        998           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_100-dataframe_dtype_int_nulls_true_cols_1_rows_100]                 440.3200 (5.84)        549.8160 (
  4.86)        450.8796 (5.74)       9.1030 (3.72)        448.2800 (5.77)       3.6877 (4.83)      131;215   2,217.8871 (0.17)       1485           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_10000-dataframe_dtype_int_nulls_false_cols_1_rows_10000]            447.4690 (5.94)      1,540.7120 (
  13.62)       461.6402 (5.88)      37.9536 (15.52)       453.8380 (5.84)       5.4100 (7.08)       73;273   2,166.1891 (0.17)       1699           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_10000-dataframe_dtype_int_nulls_true_cols_1_rows_10000]             460.7480 (6.11)        566.0440 (
  5.00)        470.0764 (5.99)      10.1033 (4.13)        467.4290 (6.02)       3.5730 (4.68)       82;170   2,127.3140 (0.17)       1310           1
  bench_to_arrow[-alt0-series_dtype_int_rows_1000000-series_dtype_int_nulls_false_rows_1000000]                                                     1,126.3140 (14.94)     4,001.5090 (
  35.37)     1,188.9982 (15.14)    239.1285 (97.79)     1,158.0040 (14.90)     55.2440 (72.28)         3;4     841.0442 (0.07)        251           1
  bench_to_arrow[index_dtype_int_nulls_false-index_dtype_int_nulls_false_rows_1000000]                                                              1,148.8420 (15.24)     1,419.3150 (
  12.54)     1,196.4333 (15.23)     59.8644 (24.48)     1,162.6880 (14.97)     68.6890 (89.88)       34;13     835.8176 (0.07)        232           1
  bench_to_arrow[-alt0-series_dtype_int_rows_1000000-series_dtype_int_nulls_true_rows_1000000]                                                      1,184.1020 (15.71)     1,341.8060 (
  11.86)     1,227.0413 (15.62)     24.8488 (10.16)     1,233.2670 (15.87)     36.4035 (47.63)      224;16     814.9685 (0.06)        707           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_100-dataframe_dtype_int_nulls_false_cols_6_rows_100]              1,494.5280 (19.83)     1,770.3150 (
  15.65)     1,535.0043 (19.55)     25.7290 (10.52)     1,527.5580 (19.66)     12.4375 (16.27)       58;72     651.4640 (0.05)        549           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_1000000-dataframe_dtype_int_nulls_false_cols_1_rows_1000000]      1,509.2440 (20.02)     1,655.6120 (
  14.63)     1,550.5675 (19.74)     24.7419 (10.12)     1,558.5480 (20.06)     40.4070 (52.87)       207;5     644.9252 (0.05)        600           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_1000000-dataframe_dtype_int_nulls_true_cols_1_rows_1000000]       1,528.6640 (20.28)     2,586.8540 (
  22.86)     1,563.9860 (19.92)     51.8342 (21.20)     1,554.2955 (20.01)     28.2585 (36.97)       17;22     639.3919 (0.05)        532           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_100-dataframe_dtype_int_nulls_true_cols_6_rows_100]               1,559.2160 (20.69)     3,025.3140 (
  26.74)     1,598.1478 (20.35)     66.7402 (27.29)     1,591.0185 (20.48)     12.6805 (16.59)       10;31     625.7244 (0.05)        500           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_10000-dataframe_dtype_int_nulls_false_cols_6_rows_10000]          1,613.0050 (21.40)     1,805.1320 (
  15.95)     1,657.0067 (21.10)     25.9876 (10.63)     1,650.8530 (21.25)     13.0320 (17.05)       43;49     603.4979 (0.05)        512           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_10000-dataframe_dtype_int_nulls_true_cols_6_rows_10000]           1,688.8090 (22.41)     1,904.4120 (
  16.83)     1,741.9332 (22.18)     51.7887 (21.18)     1,720.9390 (22.15)     16.2475 (21.26)       60;80     574.0748 (0.05)        405           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_1000000-dataframe_dtype_int_nulls_true_cols_6_rows_1000000]      25,422.1200 (337.29)   28,614.7160 (
  252.90)   25,773.8451 (328.19)   646.8112 (264.51)   25,519.0740 (328.46)   334.4718 (437.64)        3;4      38.7990 (0.00)         39           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_1000000-dataframe_dtype_int_nulls_false_cols_6_rows_1000000]     25,639.0250 (340.17)   27,578.5950 (
  243.75)   25,845.4651 (329.10)   334.4637 (136.78)   25,712.0670 (330.94)   279.7365 (366.02)        2;2      38.6915 (0.00)         43           1
  -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  ---------------------------------------------------------------------------------------------------------------------------------------------------
After
 --------------------------------------------------------------------------------------------------------------------------------------------------------- benchmark: 21 tests --[0/0]
  ---------------------------------------------------------------------------------------------------------------------------------------------------
  Name (time in us)                                                                                                                                        Min                    Max
                   Mean              StdDev                 Median                 IQR            Outliers          OPS            Rounds  Iterations
  -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  ---------------------------------------------------------------------------------------------------------------------------------------------------
  bench_to_arrow[index_dtype_int_nulls_false-index_dtype_int_nulls_false_rows_100]                                                                     90.7130 (1.0)       1,757.8600 (
  10.58)        94.0057 (1.0)       20.9559 (9.18)         92.6750 (1.0)        0.8499 (1.0)        99;645  10,637.6507 (1.0)        6839           1
  bench_to_arrow[-alt0-series_dtype_int_rows_100-series_dtype_int_nulls_false_rows_100]                                                                90.9110 (1.00)        209.5820 (
  1.26)         98.7616 (1.05)      11.5487 (5.06)         93.6195 (1.01)       6.7870 (7.99)      253;275  10,125.3878 (0.95)       2062           1
  bench_to_arrow[-alt0-series_dtype_int_rows_100-series_dtype_int_nulls_true_rows_100]                                                                100.9090 (1.11)        631.2070 (
  3.80)        107.4134 (1.14)      12.6694 (5.55)        103.1570 (1.11)       4.1385 (4.87)      511;623   9,309.8243 (0.88)       4437           1
  bench_to_arrow[-alt0-series_dtype_int_rows_10000-series_dtype_int_nulls_false_rows_10000]                                                           109.7640 (1.21)        205.1420 (
  1.23)        115.2583 (1.23)       8.6179 (3.78)        111.9695 (1.21)       2.6540 (3.12)      602;740   8,676.1631 (0.82)       4706           1
  bench_to_arrow[index_dtype_int_nulls_false-index_dtype_int_nulls_false_rows_10000]                                                                  109.9000 (1.21)        166.1190 (
  1.0)         112.0307 (1.19)       2.2823 (1.0)         111.6250 (1.20)       0.9410 (1.11)      252;306   8,926.1256 (0.84)       5848           1
  bench_to_arrow[-alt0-series_dtype_int_rows_10000-series_dtype_int_nulls_true_rows_10000]                                                            119.2890 (1.32)      2,176.6870 (
  13.10)       128.6318 (1.37)      34.8307 (15.26)       121.8230 (1.31)       5.2625 (6.19)      145;822   7,774.1247 (0.73)       4145           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_100-dataframe_dtype_int_nulls_false_cols_1_rows_100]                449.6480 (4.96)        642.1100 (
  3.87)        462.3626 (4.92)      18.8520 (8.26)        456.1150 (4.92)       5.1760 (6.09)       74;122   2,162.8049 (0.20)        778           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_100-dataframe_dtype_int_nulls_true_cols_1_rows_100]                 465.8620 (5.14)        747.1230 (
  4.50)        481.2789 (5.12)      26.6152 (11.66)       471.2620 (5.09)       9.1069 (10.71)     125;259   2,077.7975 (0.20)       1430           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_10000-dataframe_dtype_int_nulls_false_cols_1_rows_10000]            469.7340 (5.18)      1,961.6740 (
  11.81)       482.2459 (5.13)      39.6894 (17.39)       476.1840 (5.14)       6.3205 (7.44)       25;263   2,073.6310 (0.19)       1651           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_10000-dataframe_dtype_int_nulls_true_cols_1_rows_10000]             484.8910 (5.35)        683.2310 (
  4.11)        496.1492 (5.28)      18.0567 (7.91)        490.3410 (5.29)       4.1687 (4.90)      120;218   2,015.5227 (0.19)       1271           1
  bench_to_arrow[index_dtype_int_nulls_false-index_dtype_int_nulls_false_rows_1000000]                                                              1,141.8360 (12.59)     1,347.8020 (
  8.11)      1,159.9186 (12.34)     24.1818 (10.60)     1,151.4610 (12.42)      9.5165 (11.20)       31;39     862.1294 (0.08)        267           1
  bench_to_arrow[-alt0-series_dtype_int_rows_1000000-series_dtype_int_nulls_false_rows_1000000]                                                     1,142.4620 (12.59)     4,372.0620 (
  26.32)     1,200.4327 (12.77)    214.1182 (93.82)     1,172.5825 (12.65)     58.3435 (68.64)         2;5     833.0329 (0.08)        232           1
  bench_to_arrow[-alt0-series_dtype_int_rows_1000000-series_dtype_int_nulls_true_rows_1000000]                                                      1,173.3400 (12.93)     1,733.3530 (
  10.43)     1,211.1100 (12.88)     53.0848 (23.26)     1,192.9200 (12.87)     33.6110 (39.54)       40;36     825.6888 (0.08)        677           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_1000000-dataframe_dtype_int_nulls_false_cols_1_rows_1000000]      1,510.2260 (16.65)     2,059.2390 (
  12.40)     1,548.3370 (16.47)     43.6204 (19.11)     1,541.0885 (16.63)     17.8185 (20.96)       42;59     645.8542 (0.06)        596           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_1-dataframe_dtype_int_cols_1_rows_1000000-dataframe_dtype_int_nulls_true_cols_1_rows_1000000]       1,553.5430 (17.13)     2,871.0410 (
  17.28)     1,620.6362 (17.24)     93.9406 (41.16)     1,591.8260 (17.18)     48.7712 (57.38)       32;37     617.0416 (0.06)        555           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_100-dataframe_dtype_int_nulls_false_cols_6_rows_100]              1,572.2640 (17.33)     3,472.8730 (
  20.91)     1,636.7098 (17.41)     92.3683 (40.47)     1,618.2230 (17.46)     15.0855 (17.75)       46;78     610.9819 (0.06)        545           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_100-dataframe_dtype_int_nulls_true_cols_6_rows_100]               1,653.0050 (18.22)     1,864.0390 (
  11.22)     1,692.6448 (18.01)     25.2920 (11.08)     1,685.4745 (18.19)     14.6215 (17.20)       46;45     590.7914 (0.06)        452           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_10000-dataframe_dtype_int_nulls_false_cols_6_rows_10000]          1,708.2300 (18.83)     4,432.6060 (
  26.68)     1,762.9782 (18.75)    126.0333 (55.22)     1,745.7730 (18.84)     16.0030 (18.83)       10;78     567.2220 (0.05)        506           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_10000-dataframe_dtype_int_nulls_true_cols_6_rows_10000]           1,772.0540 (19.53)     1,997.5620 (
  12.02)     1,828.1129 (19.45)     43.9454 (19.26)     1,815.0000 (19.58)     14.7850 (17.40)       48;67     547.0122 (0.05)        422           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_1000000-dataframe_dtype_int_nulls_false_cols_6_rows_1000000]     24,775.0049 (273.11)   27,135.9580 (
  163.35)   25,143.2256 (267.46)   330.3610 (144.75)   25,065.0645 (270.46)   127.6605 (150.20)        3;3      39.7721 (0.00)         44           1
  bench_to_arrow[-alt1-dataframe_dtype_int_cols_6-dataframe_dtype_int_cols_6_rows_1000000-dataframe_dtype_int_nulls_true_cols_6_rows_1000000]      25,171.5480 (277.49)   26,872.6820 (
  161.77)   25,420.6705 (270.42)   342.5740 (150.10)   25,237.1850 (272.32)   453.5485 (533.62)        5;1      39.3381 (0.00)         39           1
  -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  ---------------------------------------------------------------------------------------------------------------------------------------------------

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CMake CMake build issue improvement Improvement / enhancement to an existing function libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change pylibcudf Issues specific to the pylibcudf package Python Affects Python cuDF API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants