惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
GRAHAM CLULEY
P
Privacy International News Feed
L
LINUX DO - 热门话题
C
Cisco Blogs
T
Tor Project blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
Project Zero
Project Zero
Attack and Defense Labs
Attack and Defense Labs
Latest news
Latest news
The Last Watchdog
The Last Watchdog
Apple Machine Learning Research
Apple Machine Learning Research
Simon Willison's Weblog
Simon Willison's Weblog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cloudbric
Cloudbric
Last Week in AI
Last Week in AI
S
Security Affairs
Google DeepMind News
Google DeepMind News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
有赞技术团队
有赞技术团队
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Security @ Cisco Blogs
V
V2EX
S
Secure Thoughts
人人都是产品经理
人人都是产品经理
月光博客
月光博客
博客园_首页
T
Troy Hunt's Blog
爱范儿
爱范儿
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Privacy & Cybersecurity Law Blog
V
Vulnerabilities – Threatpost

Lei Mao's Log Book

2026 FIFA World Cup 备受嘲讽的会徽 Python Debugging Via VS Code In Docker Container Vargas Plateau Regional Park 徒步 Vargas Plateau Regional Park Apex 2026 FIFA World Cup 小组赛赛程 Retaining EXIF Metadata In GIMP San Francisquito Creek Joint Powers Authority 2025 Calendar Photo 麦当劳 The FIFA World Cup 套餐 Ardenwood Historic Farm 徒步 Ardenwood Historic Farm Synchronizations With TorchRec KeyedJaggedTensor Pacific Commons Linear Park 徒步 Pacific Commons Linear Park 2026 San Jose Half Marathon 竞赛 目标 Mountain View Shoreline Park 徒步 Mountain View Shoreline Park PyTorch AOTInductor Hybrid Lowering Carquinez Strait Regional Shoreline 徒步 Carquinez Strait Regional Shoreline PyTorch Triton Kernel Transparent Tracing and Compilation 脸庞 PyTorch Fake Export 2026 BRAIN Foundation 10K 竞赛 2026 Wild and Scenic Film Festival 参观 2026 Wild and Scenic Film Festival 系统工程程序员修 Bug FIFA 官方网站的语言 PyTorch Custom Operation 汉堡王 The Mandalorian and Grogu 套餐 Tilden Regional Parks Botanic Garden 参观 Tilden Regional Park Tilden Regional Parks Botanic Garden Tilden Regional Park 徒步 《寻秦记》电影版 ICML 2026 Area Chair Experience 2026 Foster City 5K Fun Run 竞赛 2026 年 3 月和 4 月该入手的模型手办 Docker Container GUI Display Using Wayland 马拉松破二 2026 Heart & Soles Run 5K 竞赛 How Is FARS, The Fully Automated Research System? 算计: 七天的死亡游戏 Lake Chabot Regional Park 徒步 Lake Chabot Regional Park 2023 年恐怖电影《感恩节》 2026 Airport Runway Run at San Carlos Airport 5K 竞赛 Page Table for Page-Locked Host Memory Don Edwards San Francisco Bay National Wildlife Refuge - Ravenswood 徒步 Don Edwards San Francisco Bay National Wildlife Refuge - Ravenswood 法外风云 PyTorch Graph Symbolic Integer Contra Costa Canal Regional Trail 徒步 Contra Costa Canal Regional Trail 娑婆诃 PyTorch Export 2026 Western Pacific 5K 竞赛 浮躁的科研和胡扯的自媒体 Connecting Logitech Devices On Linux 2026 Oakland Half Marathon 竞赛 Del Valle Regional Park 徒步 Del Valle Regional Park CUDA_LAUNCH_BLOCKING=1 踏切时间 Wildcat Canyon Regional Park 徒步 Wildcat Canyon Regional Park Credit Card Unauthorized Transaction While In Possession 莎拉的真伪人生 2026 Union City Superhero Fun Run 5K 竞赛 McLaughlin Eastshore State Park 徒步 McLaughlin Eastshore State Park Cloudflare Worker Proxy R2 Bucket Access Zorro 和 Batman Fix MacBook Pro Space Key Stuck Problem 2026 年 1 月和 2 月该入手的模型手办 2026 Brazen Victory 10K 竞赛 儿时的玩伴李峰 Marsh Creek Regional Trail 徒步 Marsh Creek Regional Trail Perfetto GPU Flow Artifacts 百万人推理 System Performance Optimizations QQ 幻想 2026 Brazen Bay Breeze 5K 竞赛 CUDA Shared Memory Bank Conflict-Free Vectorized Access Dota 闪电站出售 Mountain View Downtown 徒步 Mountain View Downtown C++ Latch and Barrier 2025 年跑步总结 2026 Rotary Mission Ten Half Marathon 竞赛 狗的素质等于人的素质 Pleasanton Ridge Regional Park 徒步 Pleasanton Ridge Regional Park Xfinity Internet 多年来的使用感受 Randomized SVD Don Castro Regional Recreation Area 徒步 Don Castro Regional Recreation Area 拖车公司的大汉们
CUDA Rendezvous Stream
2026-01-26 · via Lei Mao's Log Book

Introduction

In CUDA programming, managing dependencies between multiple streams can be challenging, especially when coordinating work between producer and consumer kernels. A common pattern involves multiple producer streams generating data that must be consumed by multiple consumer streams. Ensuring that consumers only start processing after all producers have completed their work is crucial for data integrity.

In this blog post, we will discuss how to implement the scheduling of multiple producer and consumer streams with and without a rendezvous stream using CUDA events and why using a rendezvous stream is more favorable.

CUDA Rendezvous Stream

Suppose we have $m$ producer streams and $n$ consumer streams. Each producer stream generates data in its own partition of a shared buffer, and each consumer stream processes data from its own partition of the same buffer. The goal is to ensure that all consumer streams wait until all producer streams have completed their work before starting their processing.

Without Rendezvous Stream

In the following implementation that does not use a rendezvous stream, each consumer stream waits for all producer events individually. Consequently, each consumer stream has to wait on $m$ events, leading to a total of $m \times n$ wait operations for $m$ producers and $n$ consumers.

no_rendezvous_stream.cu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <cuda_runtime.h>
#include <iostream>
#include <vector>

#define CHECK_CUDA_ERROR(val) check((val), #val, __FILE__, __LINE__)
void check(cudaError_t err, char const* func, char const* file, int line)
{
if (err != cudaSuccess)
{
std::cerr << "CUDA Runtime Error at: " << file << ":" << line
<< std::endl;
std::cerr << cudaGetErrorString(err) << " " << func << std::endl;
}
}


__global__ void producer_kernel(int* buffer, int partition_start,
int partition_size)
{
unsigned int const idx{blockIdx.x * blockDim.x + threadIdx.x};
if (idx < partition_size)
{
buffer[partition_start + idx] += 1;
}
}


__global__ void consumer_kernel(int* buffer, int partition_start,
int partition_size)
{
unsigned int const idx{blockIdx.x * blockDim.x + threadIdx.x};
if (idx < partition_size)
{
buffer[partition_start + idx] *= 10;
}
}

int main()
{
int const m{4};
int const n{2};
int const buffer_size{10485760};


std::vector<int> h_buffer(buffer_size, 0);
int* d_buffer;
CHECK_CUDA_ERROR(cudaMalloc(&d_buffer, buffer_size * sizeof(int)));
CHECK_CUDA_ERROR(cudaMemcpy(d_buffer, h_buffer.data(),
buffer_size * sizeof(int),
cudaMemcpyHostToDevice));


std::vector<cudaStream_t> producer_streams(m);
std::vector<cudaStream_t> consumer_streams(n);
std::vector<cudaEvent_t> producer_events(m);

for (int i{0}; i < m; i++)
{
CHECK_CUDA_ERROR(cudaStreamCreate(&producer_streams[i]));
CHECK_CUDA_ERROR(cudaEventCreate(&producer_events[i]));
}

for (int i{0}; i < n; i++)
{
CHECK_CUDA_ERROR(cudaStreamCreate(&consumer_streams[i]));
}


int const producer_partition_size{buffer_size / m};
int const threads_per_block{256};

std::cout << "Launching " << m << " producers..." << std::endl;
for (int i{0}; i < m; i++)
{
int const partition_start{i * producer_partition_size};
int const blocks{(producer_partition_size + threads_per_block - 1) /
threads_per_block};

producer_kernel<<<blocks, threads_per_block, 0, producer_streams[i]>>>(
d_buffer, partition_start, producer_partition_size);
CHECK_CUDA_ERROR(cudaGetLastError());


CHECK_CUDA_ERROR(
cudaEventRecord(producer_events[i], producer_streams[i]));

std::cout << " Producer " << i << " working on elements ["
<< partition_start << ", "
<< partition_start + producer_partition_size - 1 << "]"
<< std::endl;
}


int const consumer_partition_size{buffer_size / n};

std::cout << std::endl << "Launching " << n << " consumers..." << std::endl;
for (int i{0}; i < n; i++)
{

for (int j{0}; j < m; j++)
{
CHECK_CUDA_ERROR(cudaStreamWaitEvent(consumer_streams[i],
producer_events[j], 0));
}

int const partition_start{i * consumer_partition_size};
int const blocks{(consumer_partition_size + threads_per_block - 1) /
threads_per_block};

consumer_kernel<<<blocks, threads_per_block, 0, consumer_streams[i]>>>(
d_buffer, partition_start, consumer_partition_size);
CHECK_CUDA_ERROR(cudaGetLastError());

std::cout << " Consumer " << i << " working on elements ["
<< partition_start << ", "
<< partition_start + consumer_partition_size - 1 << "]"
<< std::endl;
}


CHECK_CUDA_ERROR(cudaDeviceSynchronize());


CHECK_CUDA_ERROR(cudaMemcpy(h_buffer.data(), d_buffer,
buffer_size * sizeof(int),
cudaMemcpyDeviceToHost));


std::cout << std::endl << "Verifying results..." << std::endl;
bool success{true};
for (int i{0}; i < buffer_size; i++)
{
int const expected{10};
if (h_buffer[i] != expected)
{
std::cerr << "Error at index " << i << ": expected " << expected
<< ", got " << h_buffer[i] << std::endl;
success = false;
break;
}
}

if (success)
{
std::cout << "SUCCESS! All elements have the expected value of 10."
<< std::endl;
std::cout << "Sample values: ";
for (int i{0}; i < std::min(10, buffer_size); i++)
{
std::cout << h_buffer[i] << " ";
}
std::cout << "..." << std::endl;
}


for (int i{0}; i < m; i++)
{
CHECK_CUDA_ERROR(cudaEventDestroy(producer_events[i]));
CHECK_CUDA_ERROR(cudaStreamDestroy(producer_streams[i]));
}

for (int i{0}; i < n; i++)
{
CHECK_CUDA_ERROR(cudaStreamDestroy(consumer_streams[i]));
}

CHECK_CUDA_ERROR(cudaFree(d_buffer));

return 0;
}

If we ever want to create a wrapper function to encapsulate the consumer launch logic, we would need to pass all producer events to that function, which can be cumbersome and less maintainable.

With Rendezvous Stream

In contrast, using a rendezvous stream allows us to centralize the synchronization logic. A rendezvous stream is a dedicated CUDA stream that waits for all producer events and then records a single barrier event. It waits for all producer events and then records a single barrier event. Each consumer stream only needs to wait for this single barrier event before proceeding. This reduces the total number of wait operations from $m \times n$ to $m + n$.

rendezvous_stream.cu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <cuda_runtime.h>
#include <iostream>
#include <vector>

#define CHECK_CUDA_ERROR(val) check((val), #val, __FILE__, __LINE__)
void check(cudaError_t err, char const* func, char const* file, int line)
{
if (err != cudaSuccess)
{
std::cerr << "CUDA Runtime Error at: " << file << ":" << line
<< std::endl;
std::cerr << cudaGetErrorString(err) << " " << func << std::endl;
}
}


__global__ void producer_kernel(int* buffer, int partition_start,
int partition_size)
{
unsigned int const idx{blockIdx.x * blockDim.x + threadIdx.x};
if (idx < partition_size)
{
buffer[partition_start + idx] += 1;
}
}


__global__ void consumer_kernel(int* buffer, int partition_start,
int partition_size)
{
unsigned int const idx{blockIdx.x * blockDim.x + threadIdx.x};
if (idx < partition_size)
{
buffer[partition_start + idx] *= 10;
}
}

int main()
{
int const m{4};
int const n{2};
int const buffer_size{10485760};


std::vector<int> h_buffer(buffer_size, 0);
int* d_buffer;
CHECK_CUDA_ERROR(cudaMalloc(&d_buffer, buffer_size * sizeof(int)));
CHECK_CUDA_ERROR(cudaMemcpy(d_buffer, h_buffer.data(),
buffer_size * sizeof(int),
cudaMemcpyHostToDevice));


std::vector<cudaStream_t> producer_streams(m);
std::vector<cudaStream_t> consumer_streams(n);
cudaStream_t rendezvous_stream;
std::vector<cudaEvent_t> producer_events(m);
cudaEvent_t barrier_event;

for (int i{0}; i < m; i++)
{
CHECK_CUDA_ERROR(cudaStreamCreate(&producer_streams[i]));
CHECK_CUDA_ERROR(cudaEventCreate(&producer_events[i]));
}

for (int i{0}; i < n; i++)
{
CHECK_CUDA_ERROR(cudaStreamCreate(&consumer_streams[i]));
}


CHECK_CUDA_ERROR(cudaStreamCreate(&rendezvous_stream));
CHECK_CUDA_ERROR(cudaEventCreate(&barrier_event));


int const producer_partition_size{buffer_size / m};
int const threads_per_block{256};

std::cout << "Launching " << m << " producers..." << std::endl;
for (int i{0}; i < m; i++)
{
int const partition_start{i * producer_partition_size};
int const blocks{(producer_partition_size + threads_per_block - 1) /
threads_per_block};

producer_kernel<<<blocks, threads_per_block, 0, producer_streams[i]>>>(
d_buffer, partition_start, producer_partition_size);
CHECK_CUDA_ERROR(cudaGetLastError());


CHECK_CUDA_ERROR(
cudaEventRecord(producer_events[i], producer_streams[i]));

std::cout << " Producer " << i << " working on elements ["
<< partition_start << ", "
<< partition_start + producer_partition_size - 1 << "]"
<< std::endl;
}


std::cout << std::endl
<< "Rendezvous stream waiting for all producers..." << std::endl;
for (int i{0}; i < m; i++)
{
CHECK_CUDA_ERROR(
cudaStreamWaitEvent(rendezvous_stream, producer_events[i], 0));
}


CHECK_CUDA_ERROR(cudaEventRecord(barrier_event, rendezvous_stream));
std::cout << "Barrier event recorded on rendezvous stream." << std::endl;


int const consumer_partition_size{buffer_size / n};

std::cout << std::endl << "Launching " << n << " consumers..." << std::endl;
for (int i{0}; i < n; i++)
{

CHECK_CUDA_ERROR(
cudaStreamWaitEvent(consumer_streams[i], barrier_event, 0));

int const partition_start{i * consumer_partition_size};
int const blocks{(consumer_partition_size + threads_per_block - 1) /
threads_per_block};

consumer_kernel<<<blocks, threads_per_block, 0, consumer_streams[i]>>>(
d_buffer, partition_start, consumer_partition_size);
CHECK_CUDA_ERROR(cudaGetLastError());

std::cout << " Consumer " << i << " working on elements ["
<< partition_start << ", "
<< partition_start + consumer_partition_size - 1 << "]"
<< std::endl;
}


CHECK_CUDA_ERROR(cudaDeviceSynchronize());


CHECK_CUDA_ERROR(cudaMemcpy(h_buffer.data(), d_buffer,
buffer_size * sizeof(int),
cudaMemcpyDeviceToHost));


std::cout << std::endl << "Verifying results..." << std::endl;
bool success{true};
for (int i{0}; i < buffer_size; i++)
{
int const expected{10};
if (h_buffer[i] != expected)
{
std::cerr << "Error at index " << i << ": expected " << expected
<< ", got " << h_buffer[i] << std::endl;
success = false;
break;
}
}

if (success)
{
std::cout << "SUCCESS! All elements have the expected value of 10."
<< std::endl;
std::cout << "Sample values: ";
for (int i{0}; i < std::min(10, buffer_size); i++)
{
std::cout << h_buffer[i] << " ";
}
std::cout << "..." << std::endl;
}


for (int i{0}; i < m; i++)
{
CHECK_CUDA_ERROR(cudaEventDestroy(producer_events[i]));
CHECK_CUDA_ERROR(cudaStreamDestroy(producer_streams[i]));
}

for (int i{0}; i < n; i++)
{
CHECK_CUDA_ERROR(cudaStreamDestroy(consumer_streams[i]));
}

CHECK_CUDA_ERROR(cudaEventDestroy(barrier_event));
CHECK_CUDA_ERROR(cudaStreamDestroy(rendezvous_stream));
CHECK_CUDA_ERROR(cudaFree(d_buffer));

return 0;
}

If we ever want to create a wrapper function to encapsulate the consumer launch logic, we only need to pass the single barrier event to that function, making it much cleaner and more maintainable.

Conclusions

In some applications, we will sometimes see a CUDA stream that only does CUDA event wait and record operations without any kernel launches. This is known as a rendezvous stream. Using a rendezvous stream can significantly simplify synchronization logic when coordinating multiple producer and consumer streams. It reduces the number of wait operations, improves code maintainability, and enhances overall performance by minimizing synchronization overhead.