Skip to content

Commit 61c049b

Browse files
committed
[primitives,benchmark] add benchmark tool for primitives
1 parent e2cdcd8 commit 61c049b

File tree

5 files changed

+332
-22
lines changed

5 files changed

+332
-22
lines changed

cmake/ConfigOptions.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ if(WIN32 AND NOT UWP)
5454
option(WITH_WIN8 "Use Windows 8 libraries" OFF)
5555
endif()
5656

57+
option(BUILD_BENCHMARK "Build benchmark tools (for debugging and development only)" OFF)
5758
option(BUILD_TESTING "Build unit tests (compatible with packaging)" OFF)
5859
cmake_dependent_option(
5960
BUILD_TESTING_INTERNAL "Build unit tests (CI only, not for packaging!)" OFF "NOT BUILD_TESTING" OFF

libfreerdp/primitives/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ endif()
9191

9292
freerdp_object_library_add(freerdp-primitives)
9393

94+
if(BUILD_BENCHMARK)
95+
add_subdirectory(benchmark)
96+
endif()
97+
9498
if(BUILD_TESTING_INTERNAL)
9599
add_subdirectory(test)
96100
endif()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# FreeRDP: A Remote Desktop Protocol Implementation
2+
# FreeRDP cmake build script
3+
#
4+
# Copyright 2025 Armin Novak <[email protected]>
5+
# Copyright 2025 Thincast Technologies GmbH
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
19+
add_executable(primitives-benchmark benchmark.c)
20+
target_link_libraries(primitives-benchmark PRIVATE winpr freerdp)
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/**
2+
* FreeRDP: A Remote Desktop Protocol Implementation
3+
* primitives benchmarking tool
4+
*
5+
* Copyright 2025 Armin Novak <[email protected]>
6+
* Copyright 2025 Thincast Technologies GmbH
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
#include <stdio.h>
22+
23+
#include <winpr/crypto.h>
24+
#include <winpr/sysinfo.h>
25+
#include <freerdp/primitives.h>
26+
27+
typedef struct
28+
{
29+
BYTE* channels[3];
30+
UINT32 steps[3];
31+
prim_size_t roi;
32+
BYTE* outputBuffer;
33+
BYTE* outputChannels[3];
34+
BYTE* rgbBuffer;
35+
UINT32 outputStride;
36+
UINT32 testedFormat;
37+
} primitives_YUV_benchmark;
38+
39+
static void primitives_YUV_benchmark_free(primitives_YUV_benchmark* bench)
40+
{
41+
if (!bench)
42+
return;
43+
44+
free(bench->outputBuffer);
45+
free(bench->rgbBuffer);
46+
47+
for (size_t i = 0; i < 3; i++)
48+
{
49+
free(bench->outputChannels[i]);
50+
free(bench->channels[i]);
51+
}
52+
53+
const primitives_YUV_benchmark empty = { 0 };
54+
*bench = empty;
55+
}
56+
57+
static primitives_YUV_benchmark primitives_YUV_benchmark_init(void)
58+
{
59+
primitives_YUV_benchmark ret = { 0 };
60+
ret.roi.width = 3840 * 4;
61+
ret.roi.height = 2160 * 4;
62+
ret.outputStride = ret.roi.width * 4;
63+
ret.testedFormat = PIXEL_FORMAT_BGRA32;
64+
65+
ret.outputBuffer = calloc(ret.outputStride, ret.roi.height);
66+
if (!ret.outputBuffer)
67+
goto fail;
68+
ret.rgbBuffer = calloc(ret.outputStride, ret.roi.height);
69+
if (!ret.rgbBuffer)
70+
goto fail;
71+
winpr_RAND(ret.rgbBuffer, 1ULL * ret.outputStride * ret.roi.height);
72+
73+
for (size_t i = 0; i < 3; i++)
74+
{
75+
ret.channels[i] = calloc(ret.roi.width, ret.roi.height);
76+
ret.outputChannels[i] = calloc(ret.roi.width, ret.roi.height);
77+
if (!ret.channels[i] || !ret.outputChannels[i])
78+
goto fail;
79+
80+
winpr_RAND(ret.channels[i], 1ull * ret.roi.width * ret.roi.height);
81+
ret.steps[i] = ret.roi.width;
82+
}
83+
84+
return ret;
85+
86+
fail:
87+
primitives_YUV_benchmark_free(&ret);
88+
return ret;
89+
}
90+
91+
static const char* print_time(UINT64 t, char* buffer, size_t size)
92+
{
93+
(void)_snprintf(buffer, size, "%u.%03u.%03u.%03u", (unsigned)(t / 1000000000ull),
94+
(unsigned)((t / 1000000ull) % 1000), (unsigned)((t / 1000ull) % 1000),
95+
(unsigned)((t) % 1000));
96+
return buffer;
97+
}
98+
99+
static BOOL primitives_YUV420_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims)
100+
{
101+
const BYTE* channels[3] = { 0 };
102+
103+
for (size_t i = 0; i < 3; i++)
104+
channels[i] = bench->channels[i];
105+
106+
for (size_t x = 0; x < 10; x++)
107+
{
108+
const UINT64 start = winpr_GetTickCount64NS();
109+
pstatus_t status =
110+
prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
111+
bench->outputStride, bench->testedFormat, &bench->roi);
112+
const UINT64 end = winpr_GetTickCount64NS();
113+
if (status != PRIMITIVES_SUCCESS)
114+
{
115+
(void)fprintf(stderr, "Running YUV420ToRGB_8u_P3AC4R failed\n");
116+
return FALSE;
117+
}
118+
const UINT64 diff = end - start;
119+
char buffer[32] = { 0 };
120+
printf("[%" PRIuz "] YUV420ToRGB_8u_P3AC4R %" PRIu32 "x%" PRIu32 " took %sns\n", x,
121+
bench->roi.width, bench->roi.height, print_time(diff, buffer, sizeof(buffer)));
122+
}
123+
124+
return TRUE;
125+
}
126+
127+
static BOOL primitives_YUV444_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims)
128+
{
129+
const BYTE* channels[3] = { 0 };
130+
131+
for (size_t i = 0; i < 3; i++)
132+
channels[i] = bench->channels[i];
133+
134+
for (size_t x = 0; x < 10; x++)
135+
{
136+
const UINT64 start = winpr_GetTickCount64NS();
137+
pstatus_t status =
138+
prims->YUV444ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
139+
bench->outputStride, bench->testedFormat, &bench->roi);
140+
const UINT64 end = winpr_GetTickCount64NS();
141+
if (status != PRIMITIVES_SUCCESS)
142+
{
143+
(void)fprintf(stderr, "Running YUV444ToRGB_8u_P3AC4R failed\n");
144+
return FALSE;
145+
}
146+
const UINT64 diff = end - start;
147+
char buffer[32] = { 0 };
148+
printf("[%" PRIuz "] YUV444ToRGB_8u_P3AC4R %" PRIu32 "x%" PRIu32 " took %sns\n", x,
149+
bench->roi.width, bench->roi.height, print_time(diff, buffer, sizeof(buffer)));
150+
}
151+
152+
return TRUE;
153+
}
154+
155+
static BOOL primitives_RGB2420_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims)
156+
{
157+
for (size_t x = 0; x < 10; x++)
158+
{
159+
const UINT64 start = winpr_GetTickCount64NS();
160+
pstatus_t status =
161+
prims->RGBToYUV420_8u_P3AC4R(bench->rgbBuffer, bench->testedFormat, bench->outputStride,
162+
bench->outputChannels, bench->steps, &bench->roi);
163+
const UINT64 end = winpr_GetTickCount64NS();
164+
if (status != PRIMITIVES_SUCCESS)
165+
{
166+
(void)fprintf(stderr, "Running RGBToYUV420_8u_P3AC4R failed\n");
167+
return FALSE;
168+
}
169+
const UINT64 diff = end - start;
170+
char buffer[32] = { 0 };
171+
printf("[%" PRIuz "] RGBToYUV420_8u_P3AC4R %" PRIu32 "x%" PRIu32 " took %sns\n", x,
172+
bench->roi.width, bench->roi.height, print_time(diff, buffer, sizeof(buffer)));
173+
}
174+
175+
return TRUE;
176+
}
177+
178+
static BOOL primitives_RGB2444_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims)
179+
{
180+
for (size_t x = 0; x < 10; x++)
181+
{
182+
const UINT64 start = winpr_GetTickCount64NS();
183+
pstatus_t status =
184+
prims->RGBToYUV444_8u_P3AC4R(bench->rgbBuffer, bench->testedFormat, bench->outputStride,
185+
bench->outputChannels, bench->steps, &bench->roi);
186+
const UINT64 end = winpr_GetTickCount64NS();
187+
if (status != PRIMITIVES_SUCCESS)
188+
{
189+
(void)fprintf(stderr, "Running RGBToYUV444_8u_P3AC4R failed\n");
190+
return FALSE;
191+
}
192+
const UINT64 diff = end - start;
193+
char buffer[32] = { 0 };
194+
printf("[%" PRIuz "] RGBToYUV444_8u_P3AC4R %" PRIu32 "x%" PRIu32 " took %sns\n", x,
195+
bench->roi.width, bench->roi.height, print_time(diff, buffer, sizeof(buffer)));
196+
}
197+
198+
return TRUE;
199+
}
200+
201+
int main(int argc, char* argv[])
202+
{
203+
WINPR_UNUSED(argc);
204+
WINPR_UNUSED(argv);
205+
primitives_YUV_benchmark bench = primitives_YUV_benchmark_init();
206+
primitives_t* opt = primitives_get();
207+
primitives_t* generic = primitives_get_generic();
208+
if (!generic || !opt)
209+
{
210+
(void)fprintf(stderr, "failed to get primitives: generic=%p, opt=%p\n", (void*)generic,
211+
(void*)opt);
212+
goto fail;
213+
}
214+
215+
printf("Running YUV420 -> RGB benchmark on generic implementation:\n");
216+
if (!primitives_YUV420_benchmark_run(&bench, generic))
217+
{
218+
(void)fprintf(stderr, "YUV420 -> RGB benchmark failed\n");
219+
goto fail;
220+
}
221+
printf("\n");
222+
223+
printf("Running YUV420 benchmark on optimized implementation:\n");
224+
if (!primitives_YUV420_benchmark_run(&bench, opt))
225+
{
226+
(void)fprintf(stderr, "YUV420 benchmark failed\n");
227+
goto fail;
228+
}
229+
printf("\n");
230+
231+
printf("Running RGB -> YUV420 benchmark on generic implementation:\n");
232+
if (!primitives_RGB2420_benchmark_run(&bench, generic))
233+
{
234+
(void)fprintf(stderr, "RGB -> YUV420 benchmark failed\n");
235+
goto fail;
236+
}
237+
printf("\n");
238+
239+
printf("Running RGB -> YUV420 benchmark on optimized implementation:\n");
240+
if (!primitives_RGB2420_benchmark_run(&bench, opt))
241+
{
242+
(void)fprintf(stderr, "RGB -> YUV420 benchmark failed\n");
243+
goto fail;
244+
}
245+
printf("\n");
246+
247+
printf("Running YUV444 -> RGB benchmark on generic implementation:\n");
248+
if (!primitives_YUV444_benchmark_run(&bench, generic))
249+
{
250+
(void)fprintf(stderr, "YUV444 -> RGB benchmark failed\n");
251+
goto fail;
252+
}
253+
printf("\n");
254+
255+
printf("Running YUV444 -> RGB benchmark on optimized implementation:\n");
256+
if (!primitives_YUV444_benchmark_run(&bench, opt))
257+
{
258+
(void)fprintf(stderr, "YUV444 -> RGB benchmark failed\n");
259+
goto fail;
260+
}
261+
printf("\n");
262+
263+
printf("Running RGB -> YUV444 benchmark on generic implementation:\n");
264+
if (!primitives_RGB2444_benchmark_run(&bench, generic))
265+
{
266+
(void)fprintf(stderr, "RGB -> YUV444 benchmark failed\n");
267+
goto fail;
268+
}
269+
printf("\n");
270+
271+
printf("Running RGB -> YUV444 benchmark on optimized implementation:\n");
272+
if (!primitives_RGB2444_benchmark_run(&bench, opt))
273+
{
274+
(void)fprintf(stderr, "RGB -> YUV444 benchmark failed\n");
275+
goto fail;
276+
}
277+
printf("\n");
278+
fail:
279+
primitives_YUV_benchmark_free(&bench);
280+
return 0;
281+
}

0 commit comments

Comments
 (0)