Другой результат с версией SSE

Я пытаюсь переписать код, чтобы использовать SSE. Однако по какой-то причине моя версия SSE дает другие результаты, чем оригинал, например 209,1 вместо 1,47 и т. Д.

Почему? Всю функцию можно найти здесь.

struct vec_ps
{
    __m128 value;

    vec_ps(){}  
    vec_ps(float value)         : value(_mm_set1_ps(value)) {}
    vec_ps(__m128 value)        : value(value)              {}
    vec_ps(const vec_ps& other) : value(other.value)        {}

    vec_ps& operator=(const vec_ps& other)
    {
        value = other.value;
        return *this;
    }

    vec_ps& operator+=(const vec_ps& other)
    {
        value = _mm_add_ps(value, other.value);
        return *this;
    }

    vec_ps& operator-=(const vec_ps& other)
    {
        value = _mm_sub_ps(value, other.value);
        return *this;
    }

    vec_ps& operator*=(const vec_ps& other)
    {
        value = _mm_mul_ps(value, other.value);
        return *this;
    }

    vec_ps& operator/=(const vec_ps& other)
    {
        value = _mm_div_ps(value, other.value);
        return *this;
    }

    static vec_ps load(float* ptr)
    {
        return vec_ps(_mm_load_ps(ptr));
    }

    static void stream(float* ptr, const vec_ps& other)
    {
        _mm_stream_ps(ptr, other.value);
    }

    void stream(float* ptr)
    {
        _mm_stream_ps(ptr, value);
    }
};

vec_ps operator+(const vec_ps& lhs, const vec_ps& rhs)
{       
    return vec_ps(lhs) += rhs;
}

vec_ps operator-(const vec_ps& lhs, const vec_ps& rhs)
{       
    return vec_ps(lhs) -= rhs;
}

vec_ps operator*(const vec_ps& lhs, const vec_ps& rhs)
{       
    return vec_ps(lhs) *= rhs;
}

vec_ps operator/(const vec_ps& lhs, const vec_ps& rhs)
{       
    return vec_ps(lhs) /= rhs;
}

void foo(/*...*/)
{   
        std::vector<float, tbb::cache_aligned_allocator<float>> ref_mu(w*h);
        std::vector<float, tbb::cache_aligned_allocator<float>> cmp_mu(w*h);
        std::vector<float, tbb::cache_aligned_allocator<float>> ref_sigma_sqd(w*h);
        std::vector<float, tbb::cache_aligned_allocator<float>> cmp_sigma_sqd(w*h);
        std::vector<float, tbb::cache_aligned_allocator<float>> sigma_both(w*h);
        int size    = w*h*sizeof(float);

        /*...*/

        float ssim_sum  = 0.0;
        float ssim_sum2 = 0.0;

        vec_ps ssim_sum_ps(0.0f);       

        for(int n = 0; n < size/16; ++n)
        {
            auto ref_mu_ps          = vec_ps::load(ref_mu.data()        + n*4);
            auto cmp_mu_ps          = vec_ps::load(cmp_mu.data()        + n*4);
            auto sigma_both_ps      = vec_ps::load(sigma_both.data()    + n*4);
            auto ref_sigma_sqd_ps   = vec_ps::load(ref_sigma_sqd.data() + n*4);
            auto cmp_sigma_sqd_ps   = vec_ps::load(cmp_sigma_sqd.data() + n*4);

            auto numerator   = (2.0f * ref_mu_ps * cmp_mu_ps + C1) * (2.0f * sigma_both_ps + C2);
            auto denominator = (ref_mu_ps*ref_mu_ps + cmp_mu_ps*cmp_mu_ps + C1) * (ref_sigma_sqd_ps + cmp_sigma_sqd_ps + C2);
            ssim_sum_ps += numerator / denominator; 
        }

        for(int n = 0; n < 4; ++n)
            ssim_sum2 += ssim_sum_ps.value.m128_f32[n];

        for (int y = 0; y < h; ++y)
        {
            int offset = y*w;
            for (int x = 0; x < w; ++x, ++offset) 
            {           
                float numerator   = (2.0f * ref_mu[offset] * cmp_mu[offset] + C1) * (2.0f * sigma_both[offset] + C2);
                float denominator = (ref_mu[offset]*ref_mu[offset] + cmp_mu[offset]*cmp_mu[offset] + C1) * (ref_sigma_sqd[offset] + cmp_sigma_sqd[offset] + C2);
                ssim_sum += numerator / denominator;                
            }
        }
        assert(ssim_sum2 == ssim_sum); // FAILS!
}

person ronag    schedule 15.01.2012    source источник
comment
Вы можете и должны сами отлаживать это. Запустите его в отладчике или добавьте вызовы printf для вывода промежуточных результатов. Когда вы выделяете шаг, который не работает должным образом, не стесняйтесь писать минимальный тестовый пример и спрашивать об этом здесь. Но вот стена кода, выяснить, что не так, - не лучший вопрос.   -  person Ben Voigt    schedule 15.01.2012
comment
@BenVoigt; Конечно, вы правы. Однако я уже сделал то, что вы предлагали перед публикацией, но не смог понять этого.   -  person ronag    schedule 15.01.2012
comment
Итак, какая строка кода дает неправильный результат? Можете ли вы удалить распределители TBB и т. Д. И упростить ситуацию?   -  person Ben Voigt    schedule 15.01.2012
comment
Есть ли гарантия, что w * h делится на четыре? Если это не так, ваша последняя итерация в версии SSE будет основана на случайных числах. Использование sizeof(float) в одном месте и 16 instead of 4 * sizeof (float) `в другом несколько сбивает с толку: почему бы не оставить размер float? Кроме того, почему версия, отличная от SSE, просто не проходит по области вместо того, чтобы пытаться следить за шириной и высотой матрицы?   -  person Dietmar Kühl    schedule 15.01.2012
comment
@ DietmarKühl: Спасибо! Это был ответ, я пропустил, что w и h не делятся на четыре. Версия без SSE была написана не мной.   -  person ronag    schedule 15.01.2012
comment
@Dietmar - сделайте свой комментарий ответом, чтобы его можно было пометить как ответ.   -  person slugster    schedule 18.01.2012
comment
@ronag - пожалуйста, не удаляйте свои вопросы, если для этого нет веских причин, особенно после того, как кто-то дал вам правильный ответ.   -  person slugster    schedule 18.01.2012


Ответы (1)


Просто комментарий выше, как кажется, ответ на вопрос: есть ли гарантия, что w * h делится на четыре? Если это не так, ваша последняя итерация в версии SSE будет основана на случайных числах. Использование sizeof (float) в одном месте и 16 вместо 4 * sizeof (float) `в другом несколько сбивает с толку: почему бы не оставить размер поплавка? Кроме того, почему версия, отличная от SSE, просто не проходит по области вместо того, чтобы пытаться следить за шириной и высотой матрицы?

person Dietmar Kühl    schedule 19.01.2012