我必須在我的圖像的每個點上應用一個變換矩陣來獲得新的點座標。如何優化矩陣3乘以3與SSE點?
爲此,我創建了一個自定義Matrix3by3
類,其中包含大小爲9的浮點數組。
到矩陣應用到每個點,首先,我創造了這個功能:
constexpr auto apply_matrix(const Matrix3by3 & m, const Vec2i & p) -> Vec2f
{
const auto x = m.at(0, 0) * p.x + m.at(0, 1) * p.y + m.at(0, 2);
const auto y = m.at(1, 0) * p.x + m.at(1, 1) * p.y + m.at(1, 2);
const auto z = m.at(2, 0) * p.x + m.at(2, 1) * p.y + m.at(2, 2);
return { x/z, y/z };
}
正如你所看到的,這個函數會做簡單的矩陣乘法沒有最後乘法,因爲在我的2D沒有Z值圖片。
這個偉大的工程,但由於這部分代碼是熱碼,我試圖去優化它,所以我創建它的SSE版本:
constexpr auto apply_matrix(const Matrix3by3 & m, const Vec2i & p) -> Vec2f
{
using SSEVec3 = union {
struct
{
float z, y, x;
};
__m128 values_ = _mm_setzero_ps();
};
const auto mvec1 = _mm_set_ps(0, m.at(0, 0), m.at(0, 1), m.at(0, 2));
const auto mvec2 = _mm_set_ps(0, m.at(1, 0), m.at(1, 1), m.at(1, 2));
const auto mvec3 = _mm_set_ps(0, m.at(2, 0), m.at(2, 1), m.at(2, 2));
const auto pvec1 = _mm_set1_ps(static_cast<float>(p.x));
const auto pvec2 = _mm_set1_ps(static_cast<float>(p.y));
auto result = SSEVec3{};
result.values_ = _mm_add_ps(_mm_add_ps(_mm_mul_ps(mvec1, pvec1), _mm_mul_ps(mvec2, pvec2)), mvec3);
return { result.x/result.z, result.y/result.z };
}
這工作過,但它是比第一個版本慢,而且由於我正在學習SSE,所以我不明白爲什麼會出現這種情況。
我對這第二個版本的想法是並行執行x,y和z值計算。
所以,這是我的問題,爲什麼SSE版本更慢,我怎麼能優化它儘可能快?
謝謝!
每當在此級別進行優化時,您應該檢查生成的彙編代碼:'gcc -S'。你可以讓你的代碼更完整:周圍的代碼是什麼樣的?你幾次從跑步中獲得什麼?另外,當試圖比較它們的性能時,我會避免聲明函數constexpr。 – dlasalle
我認爲這可能是由z最終劃分的主宰。無論如何,你應該首先看看你的明顯版本的反彙編輸出。考慮到現代C++風格,你可能正在使用一個非常新近的編譯器,其中可能會啓用自動矢量化。 根據你有什麼芯片,使用mm_set_ps和m.at的手動存儲也會殺了你。 SSE沒有「收集」指令,因此做一個未對齊的加載,然後掩蓋掉最低的單詞或者忽略結果。 – cnettel
以這種方式濫用'_mm_set_ps'是一種主要的反模式,應該確實是一個適當的寬負載(如果需要,它會加載一個通道太多,填充)。 – harold