简体   繁体   English

C中的qsort是我程序中意外的瓶颈

[英]qsort in C is an unexpected bottle neck in my program

I am working on some code to align astronomical images. 我正在编写一些代码以对齐天文图像。 I am writing it in C99. 我正在C99中编写它。 For some reason, my algorithm for detecting the stars in an image was running much slower than expected. 由于某种原因,我用于检测图像中星星的算法的运行速度比预期的慢得多。 I basically need to ignore all pixels which are below the 99th percentile (because stars are just small bright points). 我基本上需要忽略所有低于99%的像素(因为星星只是很小的亮点)。 To calculate the 99th percentile I applied a qsort to a copy of the pixels in the image. 为了计算第99个百分位数,我对图像中的像素副本应用了qsort When I profiled the code, it said that it was spending 75% of the time executing the compare_quantum function used by qsort. 当我分析代码时,它说它花了75%的时间执行qsort使用的compare_quantum函数。 The entire detection of stars takes about 3 seconds. 整个恒星探测大约需要3秒钟。

I had originally written the code in C++ and the same algorithm took about 0.2 seconds. 我最初用C ++编写代码,同一算法花了大约0.2秒。 I am guessing that the reason this is happening is that unlike C++, C can't just inline the call to the compare function like C++ can with std::sort . 我猜想发生这种情况的原因是,与C ++不同,C不能像std::sort那样内联对C ++的比较函数的调用。

I could write my own sort function, but I was just wondering if anyone had any other ideas to make this go faster. 我可以编写自己的排序函数,但我只是想知道是否有人有其他想法可以使此过程更快。 I have calls to qsort elsewhere in the code and I am thinking maybe I need to get rid of all of them. 我在代码的其他地方调用了qsort,我在想也许我需要摆脱所有这些。

I am using gcc 5.2. 我正在使用gcc 5.2。 The first qsort in stars_map is the bottle neck. stars_map的第一个qsort是瓶颈。 Also quantum_t is just a typedef for uint16_t . 此外quantum_t只是uint16_t的typedef。

int compare_quantum(const void* a, const void* b)
{
    return (*(quantum_t*)a > *(quantum_t*)b) - (*(quantum_t*)a < *(quantum_t*)b);
}

void star_register(quantum_t* grey, double* rowint, double* colint, double* lum, size_t row, size_t col, size_t w, size_t h)
{
    size_t gi = row * w + col;
    if(!(row >= 0 && col >= 0 && row < h && col < w && grey[gi]))
        return;

    *rowint += grey[gi] * row;
    *colint += grey[gi] * col;
    *lum += grey[gi];
    grey[gi] = 0;

    for(int dr = -1; dr <= 1; dr++)
    {
        for(int dc = -1; dc <= 1; dc++)
        {
            if(dc == 0 && dr == 0)
                continue;
            star_register(grey, rowint, colint, lum, row + dr, col + dc, w, h);
        }
    }
}

stars_t* stars_map(image_t* img, float detection_percentile)
{
    assert(img);

    quantum_t* grey = NULL;
    quantum_t* sorted = NULL;
    star_t* stars = NULL;
    size_t nstars = 0;
    size_t stars_alloc = 0;

    grey = malloc(sizeof(quantum_t) * img->w * img->h);
    if(grey == NULL) goto fail;

    sorted = malloc(sizeof(quantum_t) * img->w * img->h);
    if(sorted == NULL) goto fail;

    for(size_t i = 0; i < img->w * img->h; i++)
        sorted[i] = grey[i] = ((uint32_t)img->px[i].red + (uint32_t)img->px[i].green + (uint32_t)img->px[i].blue) / 3;
    //this qsort is the issue
    qsort(sorted, img->w * img->h, sizeof(quantum_t), compare_quantum);
    quantum_t cut = sorted[(size_t)(img->w * img->h * detection_percentile)];
    free(sorted);
    sorted = NULL;

    for(size_t i = 0; i < img->w * img->h; i++)
        grey[i] = clampq((int32_t)grey[i] - cut);

    for(size_t i = 0; i < img->h; i++)
    {
        for(size_t j = 0; j < img->w; j++)
        {
            if(grey[i * img->w + j])
            {
                if(nstars == stars_alloc)
                {
                    stars = realloc(stars, (stars_alloc += 500) * sizeof(star_t));
                    if(!stars) goto fail;
                }
                double rowint = 0.0;
                double colint = 0.0;
                double lum = 0.0;
                star_register(grey, &rowint, &colint, &lum, i, j, img->w, img->h);
                stars[nstars++] = (star_t){.x = colint / lum, .y = rowint / lum, .lum = lum};
            }
        }
    }

    free(grey);

    qsort(stars, nstars, sizeof(star_t), star_compare);

    stars_t* result = malloc(sizeof(stars_t) + nstars * sizeof(star_t));
    if(result == NULL) goto fail;
    result->npairs = nstars;
    memcpy(result->stars, stars, sizeof(star_t) * nstars);
    free(stars);


    return result;

    fail:
    if(grey) free(grey);
    if(sorted) free(sorted);
    if(stars) free(stars);

    return NULL;
}

I originally thought that the recursive call to star_register would be the performance hit, but it barely matters in the profile. 我最初以为,对star_register的递归调用会影响性能,但是在配置文件中几乎没有关系。

The issue was that I had forgotten that I was using std::nth_element not std::sort in the c++ version. 问题是我忘记了我在c ++版本中使用的是std :: nth_element而不是std :: sort。 That is why the code was slow. 这就是为什么代码很慢的原因。 I wrote a qselect and now the entire program is about the same speed. 我写了一个qselect,现在整个程序的速度差不多。

quantum_t quantum_qselect(quantum_t *v, size_t len, size_t k)
{
    size_t i, st;

    for(st = i = 0; i < len - 1; i++)
    {
        if(v[i] > v[len - 1])
            continue;
        swap(quantum_t, v[i], v[st]);
        st++;
    }

    swap(quantum_t, v[len - 1], v[st]);

    return k == st ? v[st] : st > k ? quantum_qselect(v, st, k) : quantum_qselect(v + st, len - st, k - st);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM