簡體   English   中英

數組中值的快速乘法

[英]Fast multiplication of values in an array

有沒有一種快速的方法來在C ++中乘以浮點數組的值,以優化此函數(其中count是4的倍數):

void multiply(float* values, float factor, int count)
{
    for(int i=0; i < count; i++)
    {
        *value *= factor;
        value++;
    }
}

解決方案必須適用於Mac OS X和Windows,Intel和非Intel。 想想SSE,矢量化,編譯器(gcc與MSVC)。

如果您希望您的代碼是跨平台的,那么您將不得不編寫與平台無關的代碼,或者您將不得不編寫#ifdef的負載。

您是否嘗試過一些手動循環展開,看看它是否有任何區別?

既然您知道count是4的倍數,那么您可以展開循環...

void multiply(float* values, float factor, int count)
{
    count = count >> 2; // count / 4
    for(int i=0; i < count ; i++)
    {
        *value *= factor;
        *(value+1) *= factor;
        *(value+2) *= factor;
        *(value+3) *= factor;
        value += 4;
    }
}

免責聲明:顯然,這不適用於iPhone,iPad,Android或其未來的等價物。

#include <mmintrin.h>
#include <xmmintrin.h>

__m128 factor4 = _mm_set1_ps(factor);
for (int i=0; i+3 < count; i += 4)
{
   __m128 data = _mm_mul_ps(_mm_loadu_ps(values), factor4);
   _mm_storeu_ps(values, data);
   values += 4;
}
for (int i=(count/4)*4; i < count; i++)
{
   *values *= factor;
   value++;
}

你有沒有想過OpenMP?

大多數現代計算機都有多核CPU,幾乎所有主流編譯器似乎都內置了OpenMP。 你幾乎不花任何成本獲得速度。

請參閱Wikipedia關於OpenMP的文章

最好的解決方案是保持簡單,讓編譯器為您優化它。 海灣合作委員會了解SSE,SSE2,altivec以及其他什么。 如果您的代碼太復雜,您的編譯器將無法在每個可能的目標上對其進行優化。

正如您所提到的,有許多架構都有SIMD擴展,SIMD可能是您在優化方面最好的選擇。 然而,它們都是特定於平台的,而C和C ++作為語言並不是SIMD友好的。

但是,您應該嘗試的第一件事是為您的給定構建啟用SIMD特定標志。 編譯器可以識別可以使用SIMD優化的模式。

接下來是在適當的情況下使用編譯器內在函數或匯編來編寫特定於平台的SIMD代碼。 但是,您應該為沒有優化版本的平台保留可移植的非SIMD實現。 #ifdef在支持它的平台上啟用SIMD。

最后,至少在ARM上但不確定在英特爾上,請注意較小的整數和浮點類型允許每個SIMD指令進行大量並行操作。

我認為,沒有太多可以做的事情會產生很大的影響。 也許你可以使用OpenMP或SSE加快速度。 但現代CPU已經非常快了。 在某些應用程序中,內存帶寬/延遲實際上是瓶頸而且會變得更糟。 我們已經有三級緩存,需要智能預取算法來避免大量延遲。 因此,考慮內存訪問模式也是有意義的。 例如,如果您實現這樣的multiplyadd並使用它,如下所示:

void multiply(float vec[], float factor, int size)
{
  for (int i=0; i<size; ++i)
    vec[i] *= factor;
}

void add(float vec[], float summand, int size)
{
  for (int i=0; i<size; ++i)
    vec[i] += summand;
}

void foo(float vec[], int size)
{
  multiply(vec,2.f,size);
  add(vec,9.f,size);
}

你基本上是在內存塊上傳遞兩次。 根據矢量的大小,它可能不適合L1緩存,在這種情況下,兩次傳遞會增加一些額外的時間。 這顯然是壞的,你應該嘗試保持內存訪問“本地”。 在這種情況下,一個循環

void foo(float vec[], int size)
{
  for (int i=0; i<size; ++i) {
    vec[i] = vec[i]*2+9;
  }
}

可能會更快。 根據經驗:嘗試線性訪問內存並嘗試“本地”訪問內存,我的意思是,嘗試重用已在L1緩存中的數據。 只是一個想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM