mirror of
https://chromium.googlesource.com/libyuv/libyuv
synced 2026-11-12 09:24:00 +08:00
[libyuv] Pilot OpenMP support for row processing
Add SetMaxThreads() and GetMaxThreads() APIs (with LIBYUV_NUM_THREADS environment variable override, defaulting to half of logical cores capped at 8 when built with -fopenmp). Enable OpenMP parallel row processing for large frames (>= 1280x720) in I420ToARGBMatrix and CopyPlane (including reshaping coalesced contiguous planes across threads), and configure thread counts for ComputeHammingDistance, ComputeSumSquareError, and CalcFrameSsim. 1280x720 Benchmarks (ms): Benchmark Reps 1T 2T 4T 8T Speedup -------------------------------------------------------- I420ToARGB_Opt 1000 272 162 88 60 4.53x CopyPlane_Opt 1000 57 42 19 14 4.07x BenchmarkPsnr_Opt 1000 92 76 56 34 2.71x BenchmarkSsim_Opt 100 518 241 147 89 5.82x R=rrwinterton@gmail.com Bug: none Test: libyuv_unittest --gtest_filter=*TestSetMaxThreads*:*I420ToARGB_Opt*:*CopyPlane_Opt*:*Ssim* Change-Id: I5ca9a2b8c68eaac870da6353c51306930d015c3a CONV=a7f09b98-93ab-4652-a8eb-8d9551361f61 TAG=agy
This commit is contained in:
parent
700f153cb8
commit
f310cb6ce1
@ -66,3 +66,4 @@ You can also repeat the test a specified number of iterations, allowing benchmar
|
|||||||
set LIBYUV_REPEAT=999
|
set LIBYUV_REPEAT=999
|
||||||
set LIBYUV_FLAGS=-1
|
set LIBYUV_FLAGS=-1
|
||||||
set LIBYUV_CPU_INFO=-1
|
set LIBYUV_CPU_INFO=-1
|
||||||
|
set LIBYUV_NUM_THREADS=4
|
||||||
|
|||||||
@ -150,6 +150,16 @@ static __inline void SetCpuFlags(int cpu_flags) {
|
|||||||
LIBYUV_API
|
LIBYUV_API
|
||||||
void CpuId(int info_eax, int info_ecx, int* cpu_info);
|
void CpuId(int info_eax, int info_ecx, int* cpu_info);
|
||||||
|
|
||||||
|
// Set maximum number of threads for OpenMP.
|
||||||
|
// 0 = auto (default: half of logical cores, capped at 8).
|
||||||
|
// Returns the number of threads configured.
|
||||||
|
LIBYUV_API
|
||||||
|
int SetMaxThreads(int num_threads);
|
||||||
|
|
||||||
|
// Get maximum number of threads for OpenMP.
|
||||||
|
LIBYUV_API
|
||||||
|
int GetMaxThreads(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
} // namespace libyuv
|
} // namespace libyuv
|
||||||
|
|||||||
@ -200,7 +200,7 @@ uint64_t ComputeHammingDistance(const uint8_t* src_a,
|
|||||||
remainder = count & (kBlockSize - 1) & ~(simd_size - 1);
|
remainder = count & (kBlockSize - 1) & ~(simd_size - 1);
|
||||||
|
|
||||||
#ifdef _OPENMP
|
#ifdef _OPENMP
|
||||||
#pragma omp parallel for reduction(+ : diff)
|
#pragma omp parallel for reduction(+ : diff) num_threads(GetMaxThreads())
|
||||||
#endif
|
#endif
|
||||||
for (i = 0; i < (count - (kBlockSize - 1)); i += kBlockSize) {
|
for (i = 0; i < (count - (kBlockSize - 1)); i += kBlockSize) {
|
||||||
diff += HammingDistance(src_a + i, src_b + i, kBlockSize);
|
diff += HammingDistance(src_a + i, src_b + i, kBlockSize);
|
||||||
@ -275,7 +275,7 @@ uint64_t ComputeSumSquareError(const uint8_t* src_a,
|
|||||||
#endif
|
#endif
|
||||||
remainder = count & (kBlockSize - 1) & ~(simd_size - 1);
|
remainder = count & (kBlockSize - 1) & ~(simd_size - 1);
|
||||||
#ifdef _OPENMP
|
#ifdef _OPENMP
|
||||||
#pragma omp parallel for reduction(+ : sse)
|
#pragma omp parallel for reduction(+ : sse) num_threads(GetMaxThreads())
|
||||||
#endif
|
#endif
|
||||||
for (i = 0; i < (count - (kBlockSize - 1)); i += kBlockSize) {
|
for (i = 0; i < (count - (kBlockSize - 1)); i += kBlockSize) {
|
||||||
sse += SumSquareError(src_a + i, src_b + i, kBlockSize);
|
sse += SumSquareError(src_a + i, src_b + i, kBlockSize);
|
||||||
@ -532,7 +532,8 @@ double CalcFrameSsim(const uint8_t* src_a,
|
|||||||
const int rem_width = tile_width & (simd_size - 1);
|
const int rem_width = tile_width & (simd_size - 1);
|
||||||
|
|
||||||
#ifdef _OPENMP
|
#ifdef _OPENMP
|
||||||
#pragma omp parallel reduction(+ : ssim_total, samples)
|
#pragma omp parallel reduction(+ : ssim_total, samples) \
|
||||||
|
num_threads(GetMaxThreads())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
align_buffer_64(row_buf, (size_t)tile_width * 2 * sizeof(uint32_t));
|
align_buffer_64(row_buf, (size_t)tile_width * 2 * sizeof(uint32_t));
|
||||||
|
|||||||
@ -13,6 +13,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <string.h> // For memcpy.
|
#include <string.h> // For memcpy.
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "libyuv/convert_from_argb.h"
|
#include "libyuv/convert_from_argb.h"
|
||||||
#include "libyuv/cpu_id.h"
|
#include "libyuv/cpu_id.h"
|
||||||
@ -155,6 +158,23 @@ int I420ToARGBMatrix(const uint8_t* src_y,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(_OPENMP)
|
||||||
|
{
|
||||||
|
int num_threads = GetMaxThreads();
|
||||||
|
int use_omp = (num_threads > 1) &&
|
||||||
|
((ptrdiff_t)width * height >= 1280 * 720) &&
|
||||||
|
(height >= num_threads);
|
||||||
|
#pragma omp parallel for schedule(static) num_threads(num_threads) if (use_omp)
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
const uint8_t* row_src_y = src_y + (ptrdiff_t)y * src_stride_y;
|
||||||
|
const uint8_t* row_src_u = src_u + (ptrdiff_t)(y >> 1) * src_stride_u;
|
||||||
|
const uint8_t* row_src_v = src_v + (ptrdiff_t)(y >> 1) * src_stride_v;
|
||||||
|
uint8_t* row_dst_argb = dst_argb + (ptrdiff_t)y * dst_stride_argb;
|
||||||
|
I422ToARGBRow(row_src_y, row_src_u, row_src_v, row_dst_argb, yuvconstants,
|
||||||
|
width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
I422ToARGBRow(src_y, src_u, src_v, dst_argb, yuvconstants, width);
|
I422ToARGBRow(src_y, src_u, src_v, dst_argb, yuvconstants, width);
|
||||||
dst_argb += dst_stride_argb;
|
dst_argb += dst_stride_argb;
|
||||||
@ -164,6 +184,7 @@ int I420ToARGBMatrix(const uint8_t* src_y,
|
|||||||
src_v += src_stride_v;
|
src_v += src_stride_v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,12 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For ArmCpuCaps() but unittested on all platforms
|
// For ArmCpuCaps() but unittested on all platforms
|
||||||
#include <stdio.h> // For fopen()
|
#include <stdio.h> // For fopen()
|
||||||
|
#include <stdlib.h> // For getenv(), atoi()
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__) && (defined(__aarch64__) || defined(__loongarch__))
|
#if defined(__linux__) && (defined(__aarch64__) || defined(__loongarch__))
|
||||||
#include <sys/auxv.h> // For getauxval()
|
#include <sys/auxv.h> // For getauxval()
|
||||||
@ -521,6 +525,54 @@ int InitCpuFlags(void) {
|
|||||||
return MaskCpuFlags(-1);
|
return MaskCpuFlags(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(_OPENMP)
|
||||||
|
static int max_threads_ = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LIBYUV_API
|
||||||
|
int SetMaxThreads(int num_threads) {
|
||||||
|
#if defined(_OPENMP)
|
||||||
|
if (num_threads <= 0) {
|
||||||
|
int procs = omp_get_num_procs();
|
||||||
|
if (procs <= 2) {
|
||||||
|
num_threads = 1;
|
||||||
|
} else if (procs <= 4) {
|
||||||
|
num_threads = 2;
|
||||||
|
} else {
|
||||||
|
num_threads = procs / 2; // Avoid hyperthreads and avoid consuming host
|
||||||
|
if (num_threads > 8) {
|
||||||
|
num_threads = 8; // Prevent memory channel saturation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max_threads_ = num_threads;
|
||||||
|
omp_set_num_threads(num_threads);
|
||||||
|
return num_threads;
|
||||||
|
#else
|
||||||
|
(void)num_threads;
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBYUV_API
|
||||||
|
int GetMaxThreads(void) {
|
||||||
|
#if defined(_OPENMP)
|
||||||
|
if (max_threads_ <= 0) {
|
||||||
|
const char* env_threads = getenv("LIBYUV_NUM_THREADS");
|
||||||
|
if (env_threads) {
|
||||||
|
int t = atoi(env_threads);
|
||||||
|
if (t > 0) {
|
||||||
|
return SetMaxThreads(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SetMaxThreads(0);
|
||||||
|
}
|
||||||
|
return max_threads_;
|
||||||
|
#else
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
} // namespace libyuv
|
} // namespace libyuv
|
||||||
|
|||||||
@ -13,6 +13,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <string.h> // for memset()
|
#include <string.h> // for memset()
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "libyuv/convert_from_argb.h" // For ArgbConstants
|
#include "libyuv/convert_from_argb.h" // For ArgbConstants
|
||||||
#include "libyuv/cpu_id.h"
|
#include "libyuv/cpu_id.h"
|
||||||
@ -24,6 +27,28 @@ namespace libyuv {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(_OPENMP)
|
||||||
|
// Reshape coalesced 1D buffer into a 2D rectangle for parallel row processing.
|
||||||
|
static void ComputeParallelShape(int total_bytes,
|
||||||
|
int num_threads,
|
||||||
|
int* out_width,
|
||||||
|
int* out_height) {
|
||||||
|
const int kAlign = 64;
|
||||||
|
if (num_threads > 1 && total_bytes >= 1280 * 720) {
|
||||||
|
int total_blocks = total_bytes / kAlign;
|
||||||
|
int blocks_per_thread = total_blocks / num_threads;
|
||||||
|
if (blocks_per_thread > 0 &&
|
||||||
|
blocks_per_thread * num_threads == total_blocks) {
|
||||||
|
*out_width = blocks_per_thread * kAlign;
|
||||||
|
*out_height = num_threads;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out_width = total_bytes;
|
||||||
|
*out_height = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Copy a plane of data
|
// Copy a plane of data
|
||||||
LIBYUV_API
|
LIBYUV_API
|
||||||
void CopyPlane(const uint8_t* src_y,
|
void CopyPlane(const uint8_t* src_y,
|
||||||
@ -46,9 +71,15 @@ void CopyPlane(const uint8_t* src_y,
|
|||||||
// Coalesce rows.
|
// Coalesce rows.
|
||||||
if (src_stride_y == width && dst_stride_y == width &&
|
if (src_stride_y == width && dst_stride_y == width &&
|
||||||
(ptrdiff_t)width * height <= INT_MAX) {
|
(ptrdiff_t)width * height <= INT_MAX) {
|
||||||
|
#if defined(_OPENMP)
|
||||||
|
int num_threads = GetMaxThreads();
|
||||||
|
ComputeParallelShape(width * height, num_threads, &width, &height);
|
||||||
|
src_stride_y = dst_stride_y = (height > 1) ? width : 0;
|
||||||
|
#else
|
||||||
width *= height;
|
width *= height;
|
||||||
height = 1;
|
height = 1;
|
||||||
src_stride_y = dst_stride_y = 0;
|
src_stride_y = dst_stride_y = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
if (src_y == dst_y && src_stride_y == dst_stride_y) {
|
if (src_y == dst_y && src_stride_y == dst_stride_y) {
|
||||||
@ -112,11 +143,25 @@ void CopyPlane(const uint8_t* src_y,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Copy plane
|
// Copy plane
|
||||||
|
#if defined(_OPENMP)
|
||||||
|
{
|
||||||
|
int num_threads = GetMaxThreads();
|
||||||
|
int use_omp = (num_threads > 1) &&
|
||||||
|
((ptrdiff_t)width * height >= 1280 * 720) &&
|
||||||
|
(height >= num_threads);
|
||||||
|
#pragma omp parallel for schedule(static) num_threads(num_threads) if (use_omp)
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
CopyRow(src_y + (ptrdiff_t)y * src_stride_y,
|
||||||
|
dst_y + (ptrdiff_t)y * dst_stride_y, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
CopyRow(src_y, dst_y, width);
|
CopyRow(src_y, dst_y, width);
|
||||||
src_y += src_stride_y;
|
src_y += src_stride_y;
|
||||||
dst_y += dst_stride_y;
|
dst_y += dst_stride_y;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBYUV_API
|
LIBYUV_API
|
||||||
|
|||||||
@ -463,4 +463,22 @@ TEST_F(LibYUVBaseTest, MAYBE_TestSetCpuFlags) {
|
|||||||
MaskCpuFlags(benchmark_cpu_info_);
|
MaskCpuFlags(benchmark_cpu_info_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(LibYUVBaseTest, TestSetMaxThreads) {
|
||||||
|
#ifdef _OPENMP
|
||||||
|
printf("_OPENMP %d\n", _OPENMP);
|
||||||
|
#endif
|
||||||
|
int default_threads = GetMaxThreads();
|
||||||
|
printf("Max Threads %d\n", default_threads);
|
||||||
|
EXPECT_GE(default_threads, 1);
|
||||||
|
|
||||||
|
int threads_1 = SetMaxThreads(1);
|
||||||
|
EXPECT_EQ(1, threads_1);
|
||||||
|
EXPECT_EQ(1, GetMaxThreads());
|
||||||
|
|
||||||
|
int restored_threads = SetMaxThreads(0);
|
||||||
|
EXPECT_GE(restored_threads, 1);
|
||||||
|
EXPECT_LE(restored_threads, 8);
|
||||||
|
EXPECT_EQ(restored_threads, GetMaxThreads());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace libyuv
|
} // namespace libyuv
|
||||||
|
|||||||
@ -61,6 +61,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
printf("Kernel Version %d.%d\n", kernelversion[0], kernelversion[1]);
|
printf("Kernel Version %d.%d\n", kernelversion[0], kernelversion[1]);
|
||||||
}
|
}
|
||||||
#endif // defined(__linux__)
|
#endif // defined(__linux__)
|
||||||
|
printf("Max Threads %d\n", GetMaxThreads());
|
||||||
|
|
||||||
#if defined(__arm__) || defined(__aarch64__)
|
#if defined(__arm__) || defined(__aarch64__)
|
||||||
int has_arm = TestCpuFlag(kCpuHasARM);
|
int has_arm = TestCpuFlag(kCpuHasARM);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user