Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

vyasr
Copy link
Contributor

@vyasr vyasr commented Apr 1, 2025

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
Copy link
Contributor

@bdice bdice left a comment

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.

Copy link
Contributor

@shrshi shrshi left a comment

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!

Copy link
Contributor

@Matt711 Matt711 left a comment

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
Contributor

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
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.

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));
Copy link
Contributor

@kingcrimsontianyu kingcrimsontianyu Apr 14, 2025

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
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
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.

Copy link
Contributor

@kingcrimsontianyu kingcrimsontianyu left a comment

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
Copy link
Contributor Author

vyasr commented Apr 16, 2025

/merge

@rapids-bot rapids-bot bot merged commit 9a2ccdf into rapidsai:branch-25.06 Apr 16, 2025
115 of 131 checks passed
@vyasr vyasr deleted the feat/arrow_data_structures_python branch April 16, 2025 00:58
@vyasr
Copy link
Contributor Author

vyasr commented Apr 24, 2025

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
Archived in project
Development

Successfully merging this pull request may close these issues.

6 participants