原文地址數組
在這裏,咱們將處理一種常見的問題:輸入的數據不是向量長度的倍數,須要處理數組開頭或者結尾的剩餘數據時。這種狀況下,NEON能夠如何處理。app
使用NEON一般都是操做長度爲4到16位的數據向量。常常地,你將會發現數組並非那些長度的倍數,你必須單獨處理這些剩餘的數據。ide
例如,你想要在每一個迭代中用NEON加載、處理及存儲8個數據,可是你的數組是21個數據長度。前兩次的迭代都可以正常進行,可是第三個迭代中,只有5個數據須要處理時,應該怎麼辦。oop
有三種方法能夠處理這些剩餘數據。根據不一樣的需求、性能及代碼大小,每種方法都不盡相同。這些方法以下,速度越快的越靠前。性能
若是你可以改變你將要處理的數組的大小,用填充數據的方式增長數組大小至下一個向量大小的倍數。這可讓你在不影響鄰近存儲的狀況下讀取和寫入數據。優化
在下面的例子中,增長數組大小至24個數據,使得第三次迭代能夠完成。spa
分配更大的數組會消耗更多的內存。若是有不少短數組,這樣的分配會帶來更多的消耗。
在末尾進行填充的數據須要被初始化,一般將其初始化爲不影響計算結果的數據。例如,若是你在對數組進行求和,新的數據必須初始化爲0。若是你在查找最小值,新的元素必須設置爲最大值。
在一些狀況下,初始化不影響填充數據可能不太好實現,如當須要查找數組數據的範圍時。code
@ r0 = input array pointer @ r1 = output array pointer @ r2 = length of data in array @ We can assume that the array length is greater than zero, is an integer @ number of vectors, and is greater than or equal to the length of data @ in the array. add r2, r2, #7 @ add (vector length-1) to the data length lsr r2, r2, #3 @ divide the length of the array by the length @ of a vector, 8, to find the number of @ vectors of data to be processed loop: subs r2, r2, #1 @ decrement the loop counter, and set flags vld1.8 {d0}, [r0]! @ load eight elements from the array pointed to @ by r0 into d0, and update r0 to point to the @ next vector ... ... @ process the input in d0 ... vst1.8 {d0}, [r1]! @ write eight elements to the output array, and @ update r1 to point to next vector bne loop @ if r2 is not equal to 0, loop
若是操做容許的話,剩餘數據能夠經過重疊操做進行處理。這將使得一些數據被處理兩次。orm
在上述的例子中,第一次迭代將處理0到7的數據,第二次迭代處理5到12,第三次迭代處理13到20.須要注意5到7的數據被處理了兩次。blog
重疊操做只有在數據不受訪問次數的影響時才能被使用,該操做必須是冪等性的。如,當你須要在數組中查找最大值的時候,能夠使用這種策略。當你須要對一個數組進行求和時,重疊的數據會被重複計算。
數據的個數必須至少可以填充一個完整的向量。
@ r0 = input array pointer @ r1 = output array pointer @ r2 = length of data in array @ We can assume that the operation is idempotent, and the array is greater @ than or equal to one vector long. ands r3, r2, #7 @ calculate number of elements left over after @ processing complete vectors using @ data length & (vector length - 1) beq loopsetup @ if the result of the ands is zero, the length @ of the data is an integer number of vectors, @ so there is no overlap, and processing can begin @ at the loop @ handle the first vector separately vld1.8 {d0}, [r0], r3 @ load the first eight elements from the array, @ and update the pointer by the number of elements @ left over ... ... @ process the input in d0 ... vst1.8 {d0}, [r1], r3 @ write eight elements to the output array, and @ update the pointer @ now, set up the vector processing loop loopsetup: lsr r2, r2, #3 @ divide the length of the array by the length @ of a vector, 8, to find the number of @ vectors of data to be processed @ the loop can now be executed as normal. the @ first few elements of the first vector will @ overlap with some of those processed above loop: subs r2, r2, #1 @ decrement the loop counter, and set flags vld1.8 {d0}, [r0]! @ load eight elements from the array, and update @ the pointer ... ... @ process the input in d0 ... vst1.8 {d0}, [r1]! @ write eight elements to the output array, and @ update the pointer bne loop @ if r2 is not equal to 0, loop
NEON提供了可以在向量中處理單個數據的加載及存儲指令。經過這些,你能夠加載一個包含一個數據的向量,進行操做,而且寫入內存中。
在上述的例子中,前兩次的迭代都正常進行,處理0到7,8到15的數據。第三次迭代須要處理5個數據,能夠在一個單獨的循環中進行處理,每次循環處理一個數據。
該方法比前面的方法都慢,由於每一個數據必須被單獨的加載、處理和存儲。
處理剩餘數據須要兩次循環,一次以向量爲單位,第二次以單個數據爲單位。這將增大代碼大小。
NEON單數據加載只改變目標數據的值而不影響其餘數據。若是你在向量化的計算中涉及到操做向量的指令,如VPADD,寄存器在加載第一個單數據時必須被初始化。
@ r0 = input array pointer @ r1 = output array pointer @ r2 = length of data in array lsrs r3, r2, #3 @ calculate the number of complete vectors to be @ processed and set flags beq singlesetup @ if there are zero complete vectors, branch to @ the single element handling code @ process vector loop vectors: subs r3, r3, #1 @ decrement the loop counter, and set flags vld1.8 {d0}, [r0]! @ load eight elements from the array and update @ the pointer ... ... @ process the input in d0 ... vst1.8 {d0}, [r1]! @ write eight elements to the output array, and @ update the pointer bne vectors @ if r3 is not equal to zero, loop singlesetup: ands r3, r2, #7 @ calculate the number of single elements to process beq exit @ if the number of single elements is zero, branch @ to exit @ process single element loop singles: subs r3, r3, #1 @ decrement the loop counter, and set flags vld1.8 {d0[0]}, [r0]! @ load single element into d0, and update the @ pointer ... ... @ process the input in d0[0] ... vst1.8 {d0[0]}, [r1]! @ write the single element to the output array, @ and update the pointer bne singles @ if r3 is not equal to zero, loop exit:
重疊和單數據處理技術可以應用在數組的起始和結束,上面的代碼可以根據須要修改。
加載和存儲地址必須對齊到cache,容許更有效的內存存取。
在Cortex-A8這要求至少16字的對齊。若是沒法對輸入和輸出數組進行對齊,你就必須處理數組最開始的部分(須要對齊的部分)及結尾的部分(未完成的向量)。
當對齊數據訪問時,記得在加載及存儲指令中使用:64 或者:128 或者:256地址限定符來優化性能。你能夠對比須要處理加載和存儲的時鐘週期的個數,使用在 Technical Reference Manual 中的數據。
Here’s the relevant page in the Cortex-A8 TRM.
在單個數據中,你能夠使用ARM指令對每一個數據進行操做。然而,用ARM和NEON指令存儲到內存的相同區域會下降性能,ARM流水線會被阻塞直到NEON流水線的完成。
通常地,你在代碼中能夠避免寫到相同的內存。