從0開始學習NEON(1)

1、前言

在上個博客中對NEON有了基礎的了解,本文將針對一個圖像下采樣的例子對NEON進行學習。

學習鏈接:CPU優化技術 - NEON 開發進階

上文鏈接:https://blog.csdn.net/weixin_42108183/article/details/136412104

2、第一個例子

現在有一張圖片,需要對UV通道的數據進行下采樣,對于同種類型的數據,相鄰的4個元素求和并求均值。示意圖如下圖所示:

在這里插入圖片描述

假定圖像數據的寬為16的整數倍,如果使用c++代碼,可以寫出下面的代碼:

void DownscaleUv(uint8_t *src, uint8_t *dst, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{//遍歷每一行的數據for (int32_t j = 0; j < dst_height; j++){	// 偶數行起始位置,uint8_t *src_ptr0 = src + src_stride * j * 2;// 奇數行起始位置uint8_t *src_ptr1 = src_ptr0 + src_stride;// 存儲起始位置uint8_t *dst_ptr = dst + dst_stride * j;// 沒一次循環計算沒for (int32_t i = 0; i < dst_width; i += 2){// U通道 (u1 + u2 + u3 + u4) / 4dst_ptr[i] = (src_ptr0[i * 2] + src_ptr0[i * 2 + 2] +src_ptr1[i * 2] + src_ptr1[i * 2 + 2]) / 4;// V通道 (v1 + v2 + v3 + v4) / 4dst_ptr[i + 1] = (src_ptr0[i * 2 + 1] + src_ptr0[i * 2 + 3] +src_ptr1[i * 2 + 1] + src_ptr1[i * 2 + 3]) / 4;}}
}

通過學習向量化編程,我們可知,數據的計算可以利用單指令多數據的方式進行加速,例如上面的例子中的內層循環,下面就使用NEON來試試吧。

3、第2個例子

為了進行向量化加速,首先需要將UV數據分離,將UV數據分離的操作在NEON中很容易進行, 使用vld2交織加載或者儲存即可。對于每一行的數據,交織加載的示意圖如下。
在這里插入圖片描述

交織加載的基本原理是按照間隔挑選數據。交織加載的例子如下所示:

void DownscaleUvNeon()
{vector<uint8_t> data;  // UVUVUVUVUV...for(int i=0;i<32;i++){data.push_back(i);}// uint8_t *src_ptr0 = (uint8_t *)data.data(); // load 第一行的數據uint8x16x2_t src;src = vld2q_u8(src_ptr0);   // 交織讀取 16 * 2 的數據,需要兩個q寄存器。auto a = src_odd.val[0];   // 一行的U數據vector<uint8_t> show_data(16);vst1q_u8 (show_data.data(),a);   // 將U數據順序儲存到內存中// 打印for(auto n : show_data){cout <<  static_cast<int>(n) << endl;  // 0,2,4,6,...}   
}
4、第3個例子

對于下UV數據采樣來說,在偶數行進行上面的交織加載,再在奇數行上進行同樣的操作。奇數行和偶數行相應的數據進行相加再求平均,即可得到最后的結果。代碼實現如下:

#include <arm_neon.h>
void DownscaleUvNeon(uint8_t *src, uint8_t *dst, int32_t src_width, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{//用于加載偶數行的源數據,2組每組16個u8類型數據,(16 * 8) * 2 = 128 * 128, 因此需要兩個q寄存器。 uint8x16x2_t v8_src0;//用于加載奇數行的源數據uint8x16x2_t v8_src1;//目的數據變量,需要一個Q寄存器uint8x8x2_t v8_dst;//目前只處理16整數倍部分的結果int32_t dst_width_align = dst_width & (-16);  //  dst_width & (-16),最大能夠整除16的數。//向量化剩余的部分需要單獨處理int32_t remain = dst_width & 15;int32_t i = 0;//外層高度循環,逐行處理for (int32_t j = 0; j < dst_height; j++){//偶數行源數據地址uint8_t *src_ptr0 = src + src_stride * j * 2;//奇數行源數據地址uint8_t *src_ptr1 = src_ptr0 + src_stride;//目的數據指針uint8_t *dst_ptr = dst + dst_stride * j;//內層循環,一次16個u8結果輸出for (i = 0; i < dst_width_align; i += 16){//提取數據,進行UV分離v8_src0 = vld2q_u8(src_ptr0); src_ptr0 += 32; // 偶數行進入下一個stridev8_src1 = vld2q_u8(src_ptr1);src_ptr1 += 32; // 奇數行行進入下一個stride//水平兩個數據相加uint16x8_t v16_u_sum0 = vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 = vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 = vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 = vpaddlq_u8(v8_src1.val[1]);//上下兩個數據相加,之后求均值v8_dst.val[0] = vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] = vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);//UV通道結果交織存儲vst2_u8(dst_ptr, v8_dst);dst_ptr += 16;}//process leftovers......}
}
5、第4個例子

當圖像的寬度不是16的整數倍,需要考慮結尾數據處理,按照鏈接里面的例子,可以分為以下幾種。

1、 padding

? 也就是將數據補齊到想要的長度,如下圖所示,比如我這里需要操作 uint8x8_t的數據,但是我的數據長度只有5,可以將數據的長度填充至8。
在這里插入圖片描述

2、Overlap

? 也就是重復利用其中的某些數據,在不填充其他數據的情況下進行,如下圖所示,當需要利用uint8x4_t來對下面的數據進行計算時,可以先將04加載到寄存器上,再將36加載到寄存器上操作。
在這里插入圖片描述

常用第二種方法對結尾數據進行處理,那么圖像下采樣的數據代碼可以寫成:

#include <arm_neon.h>void DownscaleUvNeon(uint8_t *src, uint8_t *dst, int32_t src_width, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{uint8x16x2_t v8_src0;uint8x16x2_t v8_src1;uint8x8x2_t v8_dst;int32_t dst_width_align = dst_width & (-16); // 最大能夠整除16的數。int32_t remain = dst_width & 15;             // 需要剩余處理的數據長度int32_t i = 0;for (int32_t j = 0; j < dst_height; j++){uint8_t *src_ptr0 = src + src_stride * j * 2;uint8_t *src_ptr1 = src_ptr0 + src_stride;uint8_t *dst_ptr = dst + dst_stride * j;// 處理完寬度為16的整數倍數據了for (i = 0; i < dst_width_align; i += 16){v8_src0 = vld2q_u8(src_ptr0);src_ptr0 += 32;v8_src1 = vld2q_u8(src_ptr1);src_ptr1 += 32;uint16x8_t v16_u_sum0 = vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 = vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 = vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 = vpaddlq_u8(v8_src1.val[1]);v8_dst.val[0] = vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] = vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);vst2_u8(dst_ptr, v8_dst);dst_ptr += 16;}// process leftover// remain 剩余需要處理的數據長度if (remain > 0){// 從后往前回退一次向量計算需要的數據長度// 有部分數據是之前處理過的,這部分的數據在這里重復計算一次src_ptr0 = src + src_stride * (j * 2) + src_width - 32; src_ptr1 = src_ptr0 + src_stride;dst_ptr = dst + dst_stride * j + dst_width - 16;v8_src0 = vld2q_u8(src_ptr0);v8_src1 = vld2q_u8(src_ptr1);uint16x8_t v16_u_sum0 = vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 = vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 = vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 = vpaddlq_u8(v8_src1.val[1]);v8_dst.val[0] = vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] = vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);vst2_u8(dst_ptr, v8_dst);}}
}

3、 single

將剩余的元素單獨處理,就是將剩余的元素利用NEON的只加載一個元素的功能,不推薦使用,因為這里又可能for循環多次。

4、將剩余的元素當作標量處理

也就是將剩下的元素直接使用c語言編程的方式進行計算。

void DownscaleUvNeonScalar(uint8_t *src, uint8_t *dst, int32_t src_width, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{uint8x16x2_t v8_src0;uint8x16x2_t v8_src1;uint8x8x2_t v8_dst;int32_t dst_width_align = dst_width & (-16);int32_t remain = dst_width & 15;int32_t i = 0;for (int32_t j = 0; j < dst_height; j++){uint8_t *src_ptr0 = src + src_stride * j * 2;uint8_t *src_ptr1 = src_ptr0 + src_stride;uint8_t *dst_ptr = dst + dst_stride * j;for (i = 0; i < dst_width_align; i += 16) // 16 items output at one time{v8_src0 = vld2q_u8(src_ptr0);src_ptr0 += 32;v8_src1 = vld2q_u8(src_ptr1);src_ptr1 += 32;uint16x8_t v16_u_sum0 = vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 = vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 = vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 = vpaddlq_u8(v8_src1.val[1]);v8_dst.val[0] = vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] = vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);vst2_u8(dst_ptr, v8_dst);dst_ptr += 16;}//process leftoversrc_ptr0 = src + src_stride * j * 2;src_ptr1 = src_ptr0 + src_stride;dst_ptr = dst + dst_stride * j;for (int32_t i = dst_width_align; i < dst_width; i += 2){dst_ptr[i] = (src_ptr0[i * 2] + src_ptr0[i * 2 + 2] +src_ptr1[i * 2] + src_ptr1[i * 2 + 2]) / 4;dst_ptr[i + 1] = (src_ptr0[i * 2 + 1] + src_ptr0[i * 2 + 3] +src_ptr1[i * 2 + 1] + src_ptr1[i * 2 + 3]) / 4;}}
}
6、總結

本次學習中通過一個下采樣的例子學習的NEON編程過程中的優勢以及將要面臨的問題,主要是剩余數據處理的方式,后面將繼續深入學習。
/ 4;

        dst_ptr[i + 1] = (src_ptr0[i * 2 + 1] + src_ptr0[i * 2 + 3] +src_ptr1[i * 2 + 1] + src_ptr1[i * 2 + 3]) / 4;}
}

}


#### 6、總結本次學習中通過一個下采樣的例子學習的NEON編程過程中的優勢以及將要面臨的問題,主要是剩余數據處理的方式,后面將繼續深入學習。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717955.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717955.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717955.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

獲取 Windows 通知中心彈窗通知內容(含工具漢化)

目錄 前言 技術原理概述 測試代碼和程序下載連接 本文出處鏈接&#xff1a;https://blog.csdn.net/qq_59075481/article/details/136440280。 前言 從 Windows 8.1 開始&#xff0c;Windows 通知現在以 Toast 而非 Balloon 形式顯示&#xff08; Bollon 通知其實現在是應用…

在ubuntu上安裝hadoop完分布式

準備工作 Xshell安裝包 Xftp7安裝包 虛擬機安裝包 Ubuntu鏡像源文件 Hadoop包 Java包 一、安裝虛擬機 創建ubuntu系統 完成之后會彈出一個新的窗口 跑完之后會重啟一下 按住首先用ctrlaltf3進入命令界面&#xff0c;輸入root&#xff0c;密碼登錄管理員賬號 按Esc 然后輸入 …

數據結構常用的字符串函數(中英雙釋)

頭文件&#xff1a;string.h 1.strchr const char * strchr ( const char * str, int character ); Locate first occurrence of character in string str C string. character Character to be located. Return Value A pointer to the first occurrence of character in s…

適用于恢復iOS數據的 10 款免費 iPhone 恢復軟件

現在&#xff0c;您可以獲得的 iPhone 的存儲容量比大多數人的筆記本電腦和臺式電腦的存儲容量還要大。雖然能夠存儲數千張高分辨率照片和視頻文件、安裝數百個應用程序并隨身攜帶大量音樂庫以供離線收聽固然很棒&#xff0c;但在一個地方擁有如此多的數據可能會帶來毀滅性的后…

2.2_5 調度算法

文章目錄 2.2_5 調度算法一、適用于早期的批處理系統&#xff08;一&#xff09;先來先服務&#xff08;FCFS&#xff0c;First Come First Serve&#xff09;&#xff08;二&#xff09;短作業優先&#xff08;SJF&#xff0c;Shortest Job First&#xff09;&#xff08;三&a…

SpringMVC總結

SpringMVC SpringMVC是隸屬于Spring框架的一部分&#xff0c;主要是用來進行Web開發&#xff0c;是對Servlet進行了封裝。 對于SpringMVC我們主要學習如下內容: SpringMVC簡介 請求與響應 REST風格 SSM整合(注解版) 攔截器 SpringMVC是處理Web層/表現層的框架&#xff…

易語言源代碼5000例

僅供學習研究交流使用 加群下載

探索MyBatis-Plus的高階用法

引言 MyBatis-Plus 是 MyBatis 的增強工具包&#xff0c;提供了許多方便快捷的功能來簡化開發&#xff0c;提高效率。除了基本的 CRUD 操作外&#xff0c;MyBatis-Plus 還提供了一些高級功能&#xff0c;本文將探討 MyBatis-Plus 的高階用法&#xff0c;幫助開發者更好地利用該…

Linux服務器搭建超簡易跳板機連接阿里云服務器

簡介 想要規范內部連接阿里云云服務器的方式&#xff0c;但是最近懶病犯了&#xff0c;先搞一個簡易式的跳板機過渡一下&#xff0c;順便在出一個教程&#xff0c;其他以后再說&#xff01; 配置方法 創建密鑰 登錄阿里云&#xff0c;找到云服務器ECS控制臺&#xff0c;點擊…

【小白友好】LeetCode 打家劫舍 III

https://leetcode.cn/problems/house-robber-iii/description/ 前言 建議還是先看看動態規劃的基礎題再看這個。動態規劃是不刷題&#xff0c;自己100%想不出來的。 基礎題&#xff1a; 23 小白想法 現在我們想遍歷的數據結構不是數組了&#xff0c;而是一顆樹。在樹上的d…

C++遞推

統計每個月兔子的總數 #include<bits/stdc.h> using namespace std; int n,sum0; void f(int); int main() {int a[1000];cin>>n;a[1]1;a[2]2;for(int i3;i<1000;i){a[i]a[i-1]a[i-2];}cout<<a[n];return 0; } void f(int n){}猴子吃桃子 #include<b…

2024年華為OD機試真題-電腦病毒感染-Python-OD統一考試(C卷)

題目描述: 一個局域網內有很多臺電腦,分別標注為0 - N-1的數字。相連接的電腦距離不一樣,所以感染時間不一樣,感染時間用t表示。 其中網絡內一個電腦被病毒感染,其感染網絡內所有的電腦需要最少需要多長時間。如果最后有電腦不會感染,則返回-1 給定一個數組times表示一個…

在Spring Boot中如何實現異常處理?

在Spring Boot中&#xff0c;異常處理可以通過幾種方式實現&#xff0c;以提高應用程序的健壯性和用戶體驗。這些方法包括使用ControllerAdvice注解、ExceptionHandler注解、實現ErrorController接口等。下面是一些實現Spring Boot異常處理的常用方法&#xff1a; 1. 使用Cont…

Git實戰(2)

git work flow ------------------------------------------------------- ---------------------------------------------------------------- 場景問題及處理 問題1&#xff1a;最近提交了 a,b,c,d記錄&#xff0c;想把b記錄刪掉其他提交記錄保留&#xff1a; git reset …

【C++ 編程指南】

C 編程指南 ■ C環境安裝■ C 基本語法■ 預定義宏■ # 和 ## 運算符■ C 引用■ C 命名空間■ 定義命名空間■ using 指令■ 嵌套的命名空間 ■ String類■ 類■ 類的static靜態成員 ■ C 繼承■ 繼承類型 public、protected 或 private■ 訪問控制和繼承■ 多繼承■ 數據抽象…

機器學習-面經

經歷了2023年的秋招&#xff0c;現在也已經入職半年了&#xff0c;空閑時間將面試中可能遇到的機器學習問題整理了一下&#xff0c;可能答案也會有錯誤的&#xff0c;希望大家能指出&#xff01;另外&#xff0c;不論是實習&#xff0c;還是校招&#xff0c;都祝福大家能夠拿到…

990-28產品經理:Different types of IT risk 不同類型的IT風險

Your IT systems and the information that you hold on them face a wide range of risks. If your business relies on technology for key operations and activities, you need to be aware of the range and nature of those threats. 您的IT系統和您在其中持有的信息面臨…

數據結構c版(2)——二叉樹

本章我們來了解一下二叉樹這一概念。 目錄 1.樹概念及結構 1.1樹的概念??????? 1.2 樹的特點&#xff1a; 1.3 樹的相關概念 1.4 樹的表示??????? 1.5 樹在實際中的運用&#xff08;表示文件系統的目錄樹結構&#xff09; 2.二叉樹概念及結構 2.1概念 …

Qt 簡約美觀的動畫 擺鐘風格 第十季

&#x1f60a; 今天給大家分享一個擺鐘風格的加載動畫 &#x1f60a; 效果如下: 最近工作忙起來了 , 后續再分享其他有趣的加載動畫吧. 一共三個文件 , 可以直接編譯運行 //main.cpp #include "LoadingAnimWidget.h" #include <QApplication> #include <Q…

【C++】用文件流的put和get成員函數讀寫文件

題目 編寫一個mycopy程序&#xff0c;實現文件復制的功能。用法是在控制臺輸入&#xff1a; mycooy 源文件名 目標文件名 參數介紹 m a i n main main 函數的參數有兩個&#xff0c;一個int類型參數和一個指針數組。 a r g c argc argc 表示參數的個數。參數為void時 a r g …