← NVIDIA Interview Insights

NVIDIA·Data Scientist·Technical Phone Screen·Senior

Senior
May 2026

Summary

Technical screen for a Data Scientist role at NVIDIA, heavily GPU-focused. The whole thing was basically a deep dive into CUDA internals and GEMM kernel optimization, which felt more like a GPU systems engineer interview than anything data science adjacent.

Questions Asked (8)

Q1

Walk through CUDA's execution model: grids, blocks, warps, and threads. How do they relate to each other and to the hardware?

System DesignTechnical Trade-offs
Author's notes

I had this down pretty well.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining the software hierarchy (grid → block → warp → thread) and then map each level to the hardware (GPU → SM → warp scheduler → CUDA core). Use a concrete example like matrix multiplication to illustrate how threads cooperate and how warps execute in lockstep.

Pro tip: Mention that warp size is 32 and that divergence within a warp serializes execution, which is a common performance pitfall. Also note that blocks are scheduled onto SMs, and multiple blocks can reside on one SM, affecting occupancy.

1. Define the software hierarchy

Explain that a CUDA kernel launches a grid of blocks, each block contains threads, and threads are grouped into warps of 32 for execution.

2. Map to hardware

Describe how the grid maps to the GPU, blocks are assigned to Streaming Multiprocessors (SMs), warps are scheduled by warp schedulers, and threads execute on CUDA cores.

3. Explain execution and synchronization

Discuss how threads within a block can synchronize and share memory, while warps execute in SIMT fashion; blocks are independent and can execute in any order.

4. Highlight performance implications

Mention how block size, warp divergence, and occupancy affect performance, and how the model enables massive parallelism.

Key Points to Mention

  • Grid: a collection of blocks, can be 1D, 2D, or 3D.
  • Block: a group of threads that can cooperate via shared memory and synchronization.
  • Warp: 32 threads that execute in lockstep (SIMT); divergence causes serialization.
  • Thread: the smallest unit of execution, mapped to a CUDA core.
  • Hardware mapping: blocks → SMs, warps → warp schedulers, threads → CUDA cores.
  • Occupancy: the ratio of active warps to maximum warps per SM, influenced by resource usage.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Describe CUDA's memory hierarchy: global, shared, registers, constant, and texture memory. What are the performance implications of each?

System DesignTechnical Trade-offs
Author's notes

Went fine until registers.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by categorizing the memory types by scope (per-thread, per-block, per-grid) and then discuss their performance characteristics in terms of latency, bandwidth, and caching. Emphasize how these differences drive optimization strategies like coalescing, tiling, and minimizing global memory traffic.

Pro tip: Relate each memory type to a concrete optimization pattern (e.g., shared memory for tiling, constant memory for broadcast) and mention that on modern GPUs, L1 and shared memory are unified, so tuning the carveout matters.

1. Categorize by scope and location

Group the memory types by their visibility: registers (per-thread), shared (per-block), and global/constant/texture (per-grid). Mention that constant and texture are read-only caches off global memory.

2. Describe performance characteristics

For each type, state latency, bandwidth, and caching behavior. For example, registers are fastest with zero latency, shared memory has low latency and high bandwidth, global memory has high latency but high bandwidth when coalesced.

3. Explain performance implications

Discuss how access patterns affect performance: coalesced global accesses, bank conflicts in shared memory, register pressure, and the benefits of constant/texture caches for specific access patterns.

4. Connect to optimization strategies

Give examples of how to use each memory type effectively: tiling with shared memory, using constant memory for broadcast reads, texture for spatial locality, and minimizing global memory traffic.

5. Mention modern GPU nuances

Note that on newer architectures (Volta+), L1 and shared memory are unified, and the carveout can be configured. Also, registers are not directly addressable, and spills hurt performance.

Key Points to Mention

  • Registers are per-thread, fastest, but limited; spills to local memory (which is global) hurt performance.
  • Shared memory is per-block, low latency, high bandwidth, but requires synchronization and can suffer from bank conflicts.
  • Global memory is per-grid, high latency, but high bandwidth when accesses are coalesced; caching in L2 helps.
  • Constant memory is per-grid, read-only, cached, and optimized for broadcast (all threads reading same address).
  • Texture memory is per-grid, read-only, cached, and optimized for 2D spatial locality; useful for irregular access patterns.
  • On modern GPUs, L1 and shared memory are unified, so the carveout between them can be tuned for performance.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

What is occupancy in CUDA and how do you reason about it when designing a kernel?

System DesignTechnical Trade-offs
Author's notes

This is where my earlier gap showed.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining occupancy as the ratio of active warps to the maximum supported warps per SM, then explain how it's influenced by register usage, shared memory, and block size. Emphasize that occupancy is a means to an end—maximizing latency hiding—and that optimal occupancy depends on the kernel's characteristics, so reasoning should involve profiling and trade-offs.

Pro tip: Mention that higher occupancy isn't always better; sometimes lower occupancy with more registers per thread yields better performance due to reduced instruction overhead and better data reuse. This shows you understand the nuanced trade-offs beyond textbook definitions.

1. Define Occupancy

Explain that occupancy is the ratio of active warps per SM to the maximum number of warps supported by that SM, and it indicates how well the GPU's latency-hiding capabilities are utilized.

2. Identify Limiting Factors

List the key hardware resources that limit occupancy: registers per thread, shared memory per block, block size, and the maximum number of blocks per SM. Mention that these are interdependent.

3. Explain the Trade-offs

Discuss how increasing occupancy can improve latency hiding but may also increase resource contention or reduce per-thread resources, potentially hurting performance. Give examples like memory-bound vs compute-bound kernels.

4. Reason About Design Choices

Describe how you would use occupancy calculators (e.g., NVIDIA's Occupancy Calculator) and profiling tools (e.g., Nsight Compute) to guide decisions on block size, register usage, and shared memory allocation.

5. Conclude with Best Practices

Summarize that the goal is to find the sweet spot for the specific kernel, often by iterating and measuring, rather than blindly maximizing occupancy.

Key Points to Mention

  • Occupancy is calculated as active warps / max warps per SM.
  • Limited by registers, shared memory, block size, and max blocks per SM.
  • Higher occupancy helps hide memory latency but may not always improve performance.
  • Use CUDA Occupancy Calculator and profiling tools to analyze and optimize.
  • Consider kernel characteristics: memory-bound kernels benefit more from high occupancy than compute-bound ones.
  • Trade-offs: more registers per thread can reduce occupancy but improve instruction-level parallelism.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

Design a naive single-precision GEMM kernel for C = A x B, then walk through how you would optimize it step by step.

System DesignAlgorithms & Data StructuresTechnical Trade-offs
Author's notes

The meat of the whole interview.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by describing a straightforward triple-loop implementation of GEMM, then systematically apply optimizations such as tiling, memory coalescing, shared memory usage, register blocking, and vectorized loads. Emphasize how each optimization addresses specific bottlenecks (memory bandwidth, latency, compute utilization) and quantify the expected performance gains.

Pro tip: Relate optimizations to the GPU memory hierarchy and roofline model—showing you understand not just what to do but why it works on NVIDIA hardware. Mention that the final optimized kernel approaches the speed of light for the given arithmetic intensity.

1. Naive Implementation

Describe a simple triple-loop GEMM where each thread computes one element of C, reading A and B directly from global memory. Highlight that this is memory-bound and suffers from uncoalesced accesses and redundant loads.

2. Tiling with Shared Memory

Introduce tiling: each thread block computes a tile of C, loading corresponding tiles of A and B into shared memory. This reduces global memory traffic and improves data reuse.

3. Thread-Level Optimization

Apply register blocking: each thread computes multiple elements of C, increasing arithmetic intensity and reducing shared memory accesses. Also ensure coalesced global memory accesses and avoid bank conflicts in shared memory.

4. Advanced Techniques

Discuss further optimizations: vectorized loads (float4), double buffering to overlap computation and memory transfers, and using wider tiles to increase reuse. Mention tuning block size and tile dimensions for specific GPU architectures.

5. Performance Analysis

Explain how to measure performance (e.g., using CUDA events, nvprof) and compare against theoretical peak (roofline model). Discuss trade-offs between occupancy, register pressure, and shared memory usage.

Key Points to Mention

  • Memory coalescing: ensuring consecutive threads access consecutive memory locations
  • Shared memory tiling: reducing global memory bandwidth requirements
  • Register blocking: increasing arithmetic intensity and reducing shared memory traffic
  • Bank conflicts: avoiding them by padding shared memory arrays
  • Vectorized loads: using float4 to maximize memory throughput
  • Roofline model: understanding whether the kernel is memory-bound or compute-bound

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

How do you choose grid and block dimensions for a GEMM kernel, and how do you handle matrix sizes that aren't multiples of your tile size?

System DesignTechnical Trade-offs
Author's notes

Boundary handling always trips me up a little.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by explaining that tile size selection balances occupancy, memory bandwidth, and compute throughput, referencing hardware constraints like shared memory and register limits. Then describe how to handle non-multiple sizes using predication or padding, emphasizing correctness and performance trade-offs.

Pro tip: Mention that you profile with tools like Nsight Compute to tune tile sizes for the specific GPU architecture, and that you often use a fallback kernel for edge cases to avoid branch divergence.

1. Understand hardware constraints

Identify the target GPU's shared memory per SM, register file size, and maximum threads per block to bound feasible tile dimensions.

2. Balance occupancy and reuse

Choose tile sizes that maximize data reuse in shared memory while maintaining enough active warps to hide latency, often using autotuning or heuristics.

3. Handle non-multiple sizes with predication

Use conditional checks in the kernel to guard loads and stores for out-of-bounds elements, ensuring correctness without separate kernels.

4. Consider padding or separate kernels

For small remainders, pad matrices to tile multiples if memory allows, or launch a specialized kernel for edge tiles to reduce divergence.

5. Validate and tune performance

Benchmark different configurations and edge-case handling strategies, using profiling to confirm that the chosen approach meets performance targets.

Key Points to Mention

  • Shared memory capacity and bank conflicts
  • Register pressure and occupancy trade-offs
  • Thread block size (e.g., 128, 256 threads) and its effect on latency hiding
  • Predication vs. padding for boundary handling
  • Impact of non-multiple sizes on memory coalescing
  • Autotuning frameworks (e.g., cuBLAS, CUTLASS) and heuristics

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q6

How would you compare your custom GEMM kernel's throughput against cuBLAS?

Technical Trade-offsProduct Analytics & Metrics
Author's notes

Short answer: profile with nvprof or Nsight, look at TFLOPS achieved vs theoretical peak, compare to cuBLAS on the same shapes.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the comparison's scope: specify the matrix dimensions, data types, and hardware (e.g., A100, H100). Then outline a rigorous benchmarking methodology that includes warm-up runs, multiple iterations, and statistical measures like median and 95th percentile. Finally, discuss how to interpret results in terms of achieved TFLOPS and efficiency relative to cuBLAS, and what factors might explain any gaps.

Pro tip: Always compare against cuBLAS with the same problem sizes and data types, and report both absolute throughput and percentage of cuBLAS achieved. This shows you understand that cuBLAS is highly optimized and that context matters more than raw numbers.

1. Define the comparison scope

Specify the exact matrix dimensions, data types (FP32, FP16, etc.), and GPU architecture. This ensures the comparison is meaningful and reproducible.

2. Establish a benchmarking protocol

Describe how you would measure performance: warm-up iterations, number of runs, synchronization, and metrics like median, mean, and standard deviation. Mention using CUDA events or nvprof/Nsight for timing.

3. Measure and compare throughput

Run both kernels under identical conditions and compute achieved TFLOPS. Compare your kernel's throughput to cuBLAS, reporting both absolute values and relative performance (e.g., 85% of cuBLAS).

4. Analyze and interpret results

Discuss possible reasons for differences: memory bandwidth, compute utilization, tiling strategy, etc. Highlight any trade-offs (e.g., custom kernel may be faster for specific shapes but slower for others).

5. Conclude with actionable insights

Summarize when your custom kernel is preferable and what optimizations could close the gap. Emphasize the importance of context and continuous benchmarking.

Key Points to Mention

  • Use of CUDA events for accurate timing and multiple iterations to reduce noise.
  • Reporting achieved TFLOPS and percentage of cuBLAS performance.
  • Consideration of matrix dimensions and data types (e.g., FP16 vs FP32).
  • Impact of memory bandwidth and compute utilization on throughput.
  • Trade-offs between custom kernel flexibility and cuBLAS's optimized libraries.
  • Importance of warm-up runs and statistical measures (median, percentiles).

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q7

How would you overlap host-to-device memory transfers with GPU compute using CUDA streams and pinned memory?

System DesignTechnical Trade-offs
Author's notes

Knew the concept: pageable vs pinned memory, async memcpy, double buffering with two streams.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by explaining the fundamental bottleneck: host-to-device transfers over PCIe are slow and block the GPU if done synchronously. Then describe how to use pinned (page-locked) memory to enable asynchronous copies via cudaMemcpyAsync, and how CUDA streams allow overlapping these transfers with kernel execution. Finally, discuss trade-offs and best practices for maximizing overlap.

Pro tip: Mention that pinned memory is a scarce resource and over-allocating it can degrade overall system performance; use it judiciously and consider using cudaHostAlloc with cudaHostAllocWriteCombined for write-only data to reduce cache pollution.

1. Identify the bottleneck

Explain that default pageable memory transfers are synchronous and block the GPU, causing idle time. Highlight that PCIe bandwidth is much lower than GPU memory bandwidth, so overlapping is critical.

2. Use pinned memory for asynchronous transfers

Describe how to allocate pinned memory with cudaMallocHost or cudaHostAlloc, which allows the GPU to directly access host memory via DMA, enabling true asynchronous copies.

3. Leverage CUDA streams for concurrency

Explain that by issuing cudaMemcpyAsync and kernel launches into different streams, the GPU can overlap data transfers with computation. Use multiple streams to pipeline data chunks.

4. Implement double buffering or chunking

Divide data into chunks and use a double-buffering scheme: while chunk N is being computed, chunk N+1 is being transferred. This keeps both the copy engine and compute units busy.

5. Discuss trade-offs and best practices

Mention that pinned memory is limited and can hurt host performance if overused. Also note that overlapping is only beneficial if compute time is comparable to transfer time; otherwise, the bottleneck shifts.

Key Points to Mention

  • Pinned memory enables DMA and asynchronous transfers, but is a limited resource.
  • CUDA streams allow concurrent execution of kernels and memory copies.
  • Double buffering or chunking is a common pattern to achieve overlap.
  • The copy engine and compute engine can work in parallel when using different streams.
  • Overlap effectiveness depends on the ratio of compute to transfer time.
  • Use cudaMemcpyAsync with pinned memory and non-default streams for true asynchrony.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q8

How do you measure achieved occupancy, arithmetic intensity, and memory bandwidth for a CUDA kernel?

System DesignProduct Analytics & Metrics
Author's notes

Talked through Nsight Compute metrics and the roofline model.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining each metric in the context of CUDA performance analysis: achieved occupancy as the ratio of active warps to maximum warps per SM, arithmetic intensity as the ratio of floating-point operations to bytes of memory traffic, and memory bandwidth as the rate of data transfer between global memory and SMs. Then explain how to measure each using NVIDIA profiling tools like Nsight Compute or nvprof, and how to interpret the results to identify bottlenecks. Emphasize that these metrics are interrelated and should be analyzed together for optimization.

Pro tip: Mention that achieved occupancy alone is not a goal; it's a means to hide latency. Focus on whether the kernel is memory-bound or compute-bound using the roofline model, and use metrics like achieved occupancy to diagnose why performance is below the roofline.

1. Define the metrics

Clearly define achieved occupancy, arithmetic intensity, and memory bandwidth, and explain their significance in CUDA performance analysis.

2. Select profiling tools

Describe the tools available for measurement, such as NVIDIA Nsight Compute, nvprof, or CUPTI, and how they provide these metrics.

3. Measure achieved occupancy

Explain how to calculate achieved occupancy using the ratio of active warps per SM to the maximum warps supported, and how to obtain this from profiling tools.

4. Measure arithmetic intensity and memory bandwidth

Detail how to compute arithmetic intensity by counting FLOPs and memory traffic, and how to measure memory bandwidth using counters for bytes transferred over time.

5. Interpret and optimize

Discuss how to use these metrics together to identify bottlenecks (e.g., memory-bound vs compute-bound) and guide optimizations like improving memory coalescing or increasing occupancy.

Key Points to Mention

  • Achieved occupancy = active warps / maximum warps per SM, obtained via profiling tools like Nsight Compute.
  • Arithmetic intensity = FLOPs / bytes accessed, used in the roofline model to determine performance limits.
  • Memory bandwidth = bytes transferred / time, measured using hardware counters for global memory load/store throughput.
  • NVIDIA Nsight Compute provides detailed metrics including achieved occupancy, memory throughput, and instruction counts.
  • The roofline model helps interpret arithmetic intensity and memory bandwidth to identify whether a kernel is memory-bound or compute-bound.
  • Optimization strategies: improve memory coalescing, increase occupancy by reducing register usage, and use shared memory to reduce global memory traffic.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.