我尝试使用GCC 9在C99中使用各种方法来计算RGB像素的亮度。
typedef enum dt_iop_toneequalizer_method_t
{
DT_TONEEQ_MEAN = 0,
DT_TONEEQ_LIGHTNESS,
DT_TONEEQ_VALUE
} dt_iop_toneequalizer_method_t;
是用户从GUI输入的内容。
然后,我有几种对应于每种情况的功能:
typedef float rgb_pixel[4] __attribute__((aligned(16)));
#pragma omp declare simd aligned(pixel:64)
static float _RGB_mean(const rgb_pixel pixel)
{
return (pixel[0] + pixel[1] + pixel[2] + pixel[3]) / 3.0f;
}
#pragma omp declare simd aligned(pixel:16)
static float _RGB_value(const rgb_pixel pixel)
{
return fmaxf(fmaxf(pixel[0], pixel[1]), pixel[2]);
}
#pragma omp declare simd aligned(pixel:16)
static float _RGB_lightness(const rgb_pixel pixel)
{
const float max_rgb = _RGB_value(pixel);
const float min_rgb = fminf(pixel[0], fminf(pixel[1], pixel[2]));
return (max_rgb + min_rgb) / 2.0f;
}
然后,图像上的循环为:
static void exposure_mask(const float *const restrict in,
float *const restrict out,
const size_t width,
const size_t height,
const dt_iop_toneequalizer_method_t method)
{
#pragma omp parallel for simd default(none) schedule(static) aligned(in, out:64)
for(size_t k = 0; k < 4 * width * height; k += 4)
{
const rgb_pixel pixel = { in[k], in[k + 1], in[k + 2], 0.0f };
out[k / 4] = RGB_light(pixel, method);
}
}
我的第一种方法是将RGB_light()
函数与method
和函数映射的开关/大小写一起使用,但这会触发每个像素的检查,这非常昂贵。
我的想法是使用方法列表或struct
,像这样:
typedef struct RGB_light
{
// Pixel intensity (method == DT_TONEEQ_MEAN)
float (*_RGB_mean)(rgb_pixel pixel);
// Pixel HSL lightness (method == DT_TONEEQ_LIGHTNESS)
float (*_RGB_lightness)(rgb_pixel pixel);
// Pixel HSV value (method == DT_TONEEQ_VALUE)
float (*_RGB_value)(rgb_pixel pixel);
} RGB_light;
然后在循环之前将所有方法初始化一次,例如
static void exposure_mask(const float *const restrict in,
float *const restrict out,
const size_t width,
const size_t height,
const dt_iop_toneequalizer_method_t method)
{
lightness_method = RGB_light[method]; // obviously wrong syntax
#pragma omp parallel for simd default(none) schedule(static) aligned(in, out:64)
for(size_t k = 0; k < 4 * width * height; k += 4)
{
const rgb_pixel pixel = { in[k], in[k + 1], in[k + 2], 0.0f };
out[k / 4] = lightness_method(pixel);
}
}
但是我没有成功地将该想法转换为实际的工作代码。
有些事情与我想在Python中执行的操作类似:
def RGB_value(pixel):
return whatever
def RGB_lightness(pixel):
return whatever
methods = { 1: RGB_value, 2: RGB_lightness }
def loop(image, method):
for pixel in image:
lightness = methods[method](pixel)
答案 0 :(得分:1)
问题的症结似乎是:
我的想法是使用方法的列表或结构,例如:
typedef struct RGB_light { // Pixel intensity (method == DT_TONEEQ_MEAN) float (*_RGB_mean)(rgb_pixel pixel); // Pixel HSL lightness (method == DT_TONEEQ_LIGHTNESS) float (*_RGB_lightness)(rgb_pixel pixel); // Pixel HSV value (method == DT_TONEEQ_VALUE) float (*_RGB_value)(rgb_pixel pixel); } RGB_light;
然后在循环之前将所有方法初始化一次,例如
static void exposure_mask(const float *const restrict in, float *const restrict out, const size_t width, const size_t height, const dt_iop_toneequalizer_method_t method) { lightness_method = RGB_light[method]; // obviously wrong syntax
,实际的问题是使用什么而不是“明显错误的语法”。但是您已经清楚地知道如何声明函数指针,并描述了基于method
进行切换的其他代码。将这些内容组合在一起的自然方法是
float (*lightness_method)(rgb_pixel pixel);
switch (method) {
case DT_TONEEQ_MEAN:
lightness_method = _RGB_mean;
break;
case DT_TONEEQ_LIGHTNESS:
lightness_method = _RGB_lightness;
break;
case DT_TONEEQ_VALUE:
lightness_method = _RGB_value;
break;
}
...,您稍后将在某处跟着...
float l = lightness_method(one_pixel);
如果在任何数组而不是结构中提供“方法”,类似的方法也适用。在这种情况下,您可以使用method
变量而不是使用switch
语句为数组建立索引。>
但是请注意,由于您似乎专注于性能,因此您可能会发现沿着这些思路的任何方法都有些乏味。通过指针间接调用函数会拒绝编译器进行优化的机会,尽管在像素循环之外解决特定函数指针可能会有所改善,但与重复查找相比,间接调用本身更可能导致您对性能的不满意。
您应该考虑间接使用exposure_mask()
的多个版本,每个版本都直接调用特定的亮度函数。至少考虑测试这样的安排。此外,由于您将需要一堆功能几乎相同的功能,因此可以考虑使用宏或编程代码生成器来生成所有功能,而不是手动编写和维护所有这些变体。
答案 1 :(得分:0)
根据@John Bollinger的回答,我尝试了此方法:
#define LOOP(fn) \
{ \
_Pragma ("omp parallel for simd default(none) schedule(static) \
firstprivate(width, height, ch, in, out) \
aligned(in, out:64)" ) \
for(size_t k = 0; k < ch * width * height; k += 4) \
{ \
const rgb_pixel pixel = { in[k], in[k + 1], in[k + 2], 0.0f }; \
out[k / ch] = fn(pixel); \
} \
break; \
}
static void exposure_mask(const float *const restrict in, float *const restrict out,
const size_t width, const size_t height, const size_t ch,
const dt_iop_toneequalizer_method_t method)
{
switch(method)
{
case DT_TONEEQ_MEAN:
LOOP(pixel_rgb_mean);
case DT_TONEEQ_LIGHTNESS:
LOOP(pixel_rgb_lightness);
case DT_TONEEQ_VALUE:
LOOP(pixel_rgb_value);
}
}
但是,结果证明它和我之前(避免使用宏)进行逐像素检查一样快(或慢……),可能是因为我使用了unswitch-loops
参数进行编译。