How to Rewrite Apache Spark UDFs for GPU Acceleration with AI Agents
https://developer.nvidia.com/blog/rewrite-apache-spark-udfs-gpu-acceleration-ai-agents/📌 讓AI Agent改寫Spark UDF,效能加速22倍以上
TL;DR:NVIDIA推出cuDF skills,用AI agent自動把GPU不友善的Spark UDF改寫成加速版本,實測22倍以上加速。
Apache Spark的宣告式查詢介面本來能讓cuDF無痛把運算丟給GPU平行處理,但只要查詢裡藏了一個user-defined function(UDF),GPU加速管線就得整條停下來,把資料轉回CPU能懂的格式,這個老問題,現在被AI agent解決得又快又系統化。
🤔 UDF為什麼會拖垮本該全程GPU加速的查詢
Spark的SQL/Dataframe抽象之所以強大,是因為它把「查詢要產生什麼」和「系統要怎麼執行」分開,讓cuDF可以在不改使用者程式碼的前提下,用GPU的SIMD能力平行處理列運算。但UDF打破了這層抽象,它包含系統無法推理、也無法一般化平行處理的任意使用者程式碼。執行時,GPU accelerator必須把資料從columnar格式轉成row-wise格式,回退到CPU逐列執行UDF,再轉回columnar格式。這個流程帶來兩個代價:一是因為fallback而產生的資料來回搬移開銷,二是錯失了用GPU加速原語改寫後本可獲得的運算加速。
🧩 三種改寫路徑,複雜度與適用情境各不相同
| 改寫方式 | 複雜度 | 最適情境 |
|---|---|---|
| SQL | 低,抽象層級最高 | 簡單的關聯邏輯,如小型或可內嵌的函式 |
| cuDF | 中,對執行有較高彈性與控制力 | 多數情境,如中等複雜的字串解析、巢狀型別 |
| CUDA | 高,可做到核心(kernel)層級的最大彈性 | 最複雜或效能最關鍵的邏輯 |
以一個把攝氏溫度轉華氏溫度的Scala UDF為例:最直接的做法是直接用Spark SQL運算式取代UDF(celsius * 9.0 / 5.0 + 32.0),讓這段邏輯在查詢計畫中可見,cuDF plugin在查詢規劃階段就會把它替換成對應的GPU實作,資料與執行全程留在GPU上。
當SQL原語不夠用時,可以改用cuDF plugin提供的RapidsUDF介面,讓UDF直接操作GPU上的欄位(column)。這個介面只要求實作一個evaluateColumnar方法,plugin會把GPU常駐的ColumnVector傳進來,函式再用cuDF的Java API組合運算,取得更豐富的處理能力。
而對於無法用現有cuDF運算組合出來的高複雜邏輯,RapidsUDF還能透過Java Native Interface(JNI)呼叫自訂的CUDA kernel,適用於最複雜或效能最關鍵的情境。
💡 cuDF skills把改寫拆成四個驗證導向的回饋迴圈
UDF改寫雖然有效能紅利,但需要仔細挑選API、做語意驗證和效能測量,而且欄位式(columnar)API對不熟悉向量化程式設計的工程師來說並不直觀,這恰好是coding agent擅長的工作。NVIDIA的cuDF skills for Apache Spark把整個改寫流程拆成四個聚焦的回饋迴圈:測試、轉換、benchmark、最佳化,每個skill都定義了要做的工作,以及進入下一階段前必須提供的證據。
這些skills還提供完整的專案範本,內建cuDF與Spark專屬的scaffolding,包括測試與benchmark harness、輸出驗證routine、build設定,讓agent能專注在UDF本身,而不用每次都重新搭建基礎設施;另外也附上cuDF GPU程式設計的最佳實踐建議,作為參考用的提示與技巧。agent可以依據情境自由嘗試不同實作,再用實際測得的正確性與效能來決定取捨。
📊 某消費科技公司把30多個生產環境UDF轉成GPU加速版
一家大型消費科技公司用這套工作流程,把正式生產資料pipeline中使用的30多個UDF,轉換成GPU加速的實作。這些改寫保留了原始CPU UDF的行為,同時移除了原本拖累整條GPU加速workload的昂貴fallback,帶來22倍以上的加速效果。
🎯 對維運大型Spark workload的工程師,這是把UDF瓶頸變成可規模化任務的機會
如果團隊的資料pipeline裡散布著大量自訂UDF,拖累了原本該由cuDF加速的查詢,這套skill提供的三層改寫路徑(SQL/cuDF/CUDA)加上agent驅動的測試與benchmark迴圈,等於把原本耗時又需要向量化程式設計經驗的改寫工作,變成一個有guardrail、可快速迭代的流程。
🔗 來源
- 標題:How to Rewrite Apache Spark UDFs for GPU Acceleration with AI Agents
- 作者/機構:Elizabeth Goodman, NVIDIA Developer
- 連結:https://developer.nvidia.com/blog/rewrite-apache-spark-udfs-gpu-acceleration-ai-agents/
#NVIDIA #cuDF #ApacheSpark #GPUAcceleration #AIAgents #BigData #DataEngineering #CUDA #PerformanceOptimization #SparkUDF
原始資料 NVIDIA Developer · 收集於 2026-10-13
摘要原文
Enterprise applications have become increasingly demanding. Agentic AI has compounded the need for vast amounts of enterprise data with frequent, complex queries across increasingly large datasets. Meeting that demand requires greater data throughput without a proportional increase in cost. NVIDIA cuDF addresses this challenge by leveraging the massive parallelism and high-bandwidth memory of GPUs for data analytics. Particularly, the NVIDIA cuDF plugin for Apache Spark brings this acceleration to Apache Spark , the industry-leading platform for big data processing and analytics, to achieve better TCO performance in major workloads. User-defined functions (UDFs) have long been problematic for accelerated query engines, introducing runtime overheads due to their opaque nature. While UDFs can often be replaced with more performant code, rewriting UDFs has long been a laborious process requiring careful validation. This post describes how to use agents to rewrite UDFs into GPU-accelerated equivalents within minutes, achieving 22x+ speedups in Apache Spark jobs . We’ll introduce the cuDF skills for Apache Spark , a set of skills to quickly break through the UDF bottleneck for end-to-end development, from testing, to benchmarking, through integration. The UDF challenge cuDF achieves seamless GPU acceleration in part due to Apache Spark’s declarative programming interface. The SQL / Dataframe abstractions are powerful because they separate what a query should produce from how the system should execute it. This enables the cuDF plugin to transparently parallelize row-wise operations using the massive SIMD capabilities of GPUs, without any change in user code. Apache Spark, however, also supports UDFs: custom routines to extend built-in functionality. While useful for implementing bespoke business logic, UDFs break the declarative abstraction—they contain arbitrary user code which the system cannot reason about or parallelize in any general sense. During execution, the GPU accelerator must convert the data from a columnar to row-wise format, and fall back to the CPU to execute the UDF row-by-row: Figure 1.A CPU UDF forces an otherwise GPU-resident pipeline to convert columnar batches to rows, execute on the CPU, and convert back to columnar format Thus in an otherwise accelerated query, UDFs have the following drawbacks: The UDF incurs a round-trip data transfer overhead due to fallback. The UDF forgoes potential compute speedup, had it been written using GPU-accelerated primitives. Accelerating UDFs With a small, targeted code change, many UDFs can be rewritten into a GPU-accelerable form by using the right primitives. Rewriting UDFs eliminates the overhead associated with accelerator fallback, and can yield speedups of over 20x . We outline three approaches to rewrite UDFs: Approach Complexity Best For SQL Low, highest level of abstraction Simple relational logic, e.g. small or inlined functions cuDF Medium, more flexibility and control over execution Most cases, e.g. moderately complex logic like string parsing, nested types CUDA High, maximum flexibility and kernel-level control Most complex or performance critical logic Table 1. Comparison of SQL, cuDF, and CUDA approaches for rewriting Spark UDFs, organized by complexity and best-fit use case To make these approaches concrete, consider a simple Scala UDF that converts temperature from Celsius to Fahrenheit: final class ToFahrenheit extends Function1[java.lang.Double, java.lang.Double] with Serializable { override def apply(celsius: java.lang.Double): java.lang.Double = { if (celsius == null) null else celsius * 9.0 / 5.0 + 32.0 } } spark.udf.register("to_fahrenheit", new ToFahrenheit) spark.sql(""" SELECT *, to_fahrenheit(celsius) AS fahrenheit FROM temperatures """) Rewriting the UDF with SQL The most direct solution for simple UDFs is to replace the UDF with an equivalent Spark SQL expression: spark.sql(""" SELECT *, celsius * 9.0 / 5.0 + 32.0 AS fahrenheit FROM temperatures """) This makes the function visible in Spark’s expression tree. During query planning, the cuDF plugin will replace these expressions with their corresponding GPU implementations, keeping the data and execution fully on the GPU. Rewriting the UDF with cuDF When SQL primitives are insufficient or undesirable, the UDF can instead operate directly on GPU columns using the RapidsUDF interface, a contract provided by the cuDF plugin. The contract requires a single method, evaluateColumnar : import ai.rapids.cudf.{ColumnVector, Scalar} import com.nvidia.spark.RapidsUDF import scala.util.Using final class ToFahrenheit extends Function1[java.lang.Double, java.lang.Double] with Serializable with RapidsUDF { override def apply(celsius: java.lang.Double): java.lang.Double = { if (celsius == null) null else celsius * 9.0 / 5.0 + 32.0 } override def evaluateColumnar( numRows: Int, args: ColumnVector*): ColumnVector = { Using.Manager { use => val scale = use(Scalar.fromDouble(9.0 / 5.0)) val offset = use(Scalar.fromDouble(32.0)) val scaled = use(args.head.mul(scale)) scaled.add(offset) }.get } } The cuDF plugin recognizes instances of the RapidsUDF interface and passes a GPU-resident ColumnVector to the evaluateColumnar function. The function then composes operations from the Java API for cuDF operations to process the column in parallel. This approach provides a richer API for more complex processing. Rewriting the UDF with CUDA For highly complex logic that cannot be efficiently composed from existing cuDF operations, a RapidsUDF can invoke a custom CUDA kernel through the Java Native Interface (JNI) . We’ve elided the code for brevity, but examples of CUDA conversions can be found in the cuDF examples for Spark UDFs . Efficient rewrites with AI agents Despite the performance benefits, UDF rewrites can require significant engineering effort. They demand careful API selection, semantic validation, and performance measurement, while columnar APIs can be unfamiliar to practitioners with less experience in vectorized programming. Fortunately, much of this work is well suited to coding agents. With proper guardrails, an agent can search unfamiliar APIs, implement candidate rewrites, and iterate quickly while executable tests and performance measurements determine whether each change is acceptable. Available skills The cuDF skills for Apache Spark decompose the rewrite into four focused feedback loops. Each skill defines both the work to perform and the evidence required before proceeding to the next stage: Figure 2. Four skills guide agents through testing, conversion, benchmarking, and optimization, with verification-driven feedback loops at each stage Scaffolding and design Reliable agent execution begins with a reliable development environment. The skills provide a complete project template containing the cuDF- and Spark-specific scaffolding required for end-to-end development. This includes test and benchmark harnesses, encoded routines for output verification, and build configuration. By encoding this knowledge into code, the agent can focus on the UDF rather than repeatedly reconstructing specialized infrastructure. The skills also provide best practices for GPU programming with cuDF, including memory management and configuration. This serves as an advisory tips and tricks reference. To best leverage the power of frontier models, the agent remains free to explore different implementations based on surrounding context and select an approach based on measured correctness and performance. Performance results A major consumer technology company used this workflow to convert 30+ UDFs used in production data pipelines into GPU-accelerated implementations. The rewrites preserved the behavior of the original CPU UDFs while removing costly fallbacks from otherwise GPU-accelerated workloads. As an example, the following benchmark shows the resulting before-and-after performance for a UDF in one such workload: Figure 3.
由 claude-code/sonnet 自動生成