日B视频 亚洲,啪啪啪网站一区二区,91色情精品久久,日日噜狠狠色综合久,超碰人妻少妇97在线,999青青视频,亚洲一区二卡,让本一区二区视频,日韩网站推荐

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

PRelu算子調優(yōu)經歷-函數(shù)優(yōu)化策略

恩智浦MCU加油站 ? 來源:NXP ? 作者:NXP ? 2023-08-24 08:50 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

上一篇小編和大家分享了在運行客戶的一個模型時遇到了一個PRelu算子,在利用TFLm自帶的PRelu參考實現(xiàn)的代碼,其中PRelu竟然拋出了188ms的天文數(shù)字...因此小編開始準備PRelu算子的優(yōu)化工作。

分析了參考實現(xiàn)后,發(fā)現(xiàn)了兩個優(yōu)化方向,其一是PRelu中alpha參數(shù)的特殊性所帶來的內存訪問優(yōu)化;以及量化模型所帶來的反量化問題。

本期小編就和大家一起來看下對于反量化問題的優(yōu)化細節(jié)。在開始前,再來回顧一下小編所特殊定制的模型:

8d384948-4217-11ee-a2ef-92fbcf53809c.png

這是一個具有5個節(jié)點的小巧的深度神經網絡,輸入時128*128*3,模型推理時間(采用Keil IDE,ofast優(yōu)化):

8d4fd5a4-4217-11ee-a2ef-92fbcf53809c.png

跳過PRelu算子,模型推理時間:

8d855300-4217-11ee-a2ef-92fbcf53809c.png

這樣我們就可以得出PRelu算子的執(zhí)行時間為13ms,接下來就將以此為基礎進行算法優(yōu)化,TFLm算法實現(xiàn):

output_value = MultiplyByQuantizedMultiplier(
                  input_value, params.output_multiplier_1, params.output_shift_1);
output_value = MultiplyByQuantizedMultiplier(
                  input_value * alpha_value, params.output_multiplier_2, params.output_shift_2);

上一篇小編給大家解釋了為何需要進行反量化操作以及其必要性。所謂反量化操作的本質,就是要用int8類型的中間結果來準確表達浮點結果。那么具體來說需要怎么操作呢?下面就是嚴謹?shù)耐乒江h(huán)節(jié),請讀友們不要眨眼:

首先是整數(shù)環(huán)節(jié),我們假設輸入為input, 輸出為output,參數(shù)alpha;其參數(shù)類型均為int8。而想要將其反量化為浮點數(shù),需要為其設定對應的量化參數(shù),分別為scale以及zero_point。這樣一來,變量的浮點數(shù)表示即為:

v_fp=scale* (v_i8+zero_point)

為了分析簡單,我們假設zero_point為0,那么上式可被簡化為,當然實際計算式,只需要將輸入值提前加上其zero_point再進行操作即可:

v_fp=scale* v_i8

接下來我們根據輸入數(shù)據的符號進行區(qū)分,當輸入為正時,其輸出結果為,

scale_o* output=scale_i* v_i8
output=scale_i  /  scale_0* v_i8

這樣我們就可以根據輸入直接獲取int8類型的輸出結果。

當輸入為負時:

scale_o* output=(scale_a*alpha)*(scale_i* v_i8)
output=((scale_a* scale_i)/scale_0)* 〖alpha*v〗_i8)

這樣也就獲得了相對應的負數(shù)輸入所對應的輸出結果。不過,征程還沒有結束,TFLm的參考實現(xiàn)會將這兩組浮點數(shù)代表的scale參數(shù)轉換為指數(shù)形式,并以mul+shift的形式保存為:正數(shù)output_multipiler_1和output_shift_1, 負數(shù)output_multipiler_2和output_shift_2。

知道了結果是如何進行反量化操作的,回過頭我們看看TFLm的實現(xiàn):

inline std::int16_t SaturatingRoundingDoublingHighMul(std::int16_t a,
                                                      std::int16_t b) {
  bool overflow = a == b && a == std::numeric_limits<std::int16_t>::min();
  std::int32_t a_32(a);
  std::int32_t b_32(b);
  std::int32_t ab_32 = a_32 * b_32;
  std::int16_t nudge = ab_32 >= 0 ? (1 << 14) : (1 - (1 << 14));
  std::int16_t ab_x2_high16 =
      static_cast<std::int16_t>((ab_32 + nudge) / (1 << 15));
  return overflow ? std::numeric_limits<std::int16_t>::max() : ab_x2_high16;
}
inline int32_t MultiplyByQuantizedMultiplier(int32_t x,
                                             int32_t quantized_multiplier,
                                             int shift) {
  using gemmlowp::RoundingDivideByPOT;
  using gemmlowp::SaturatingRoundingDoublingHighMul;
  int left_shift = shift > 0 ? shift : 0;
  int right_shift = shift > 0 ? 0 : -shift;
  return RoundingDivideByPOT(SaturatingRoundingDoublingHighMul(
                                 x * (1 << left_shift), quantized_multiplier),
                             right_shift);
}

首先arm的cmsis-nn庫是兼容這種量化方式的,那么他也一定有一個這樣的實現(xiàn),功夫不負有心人,這個函數(shù)叫做arm_nn_requantize,直接替換MultiplyByQuantizedMultiplier函數(shù)讓我們先看一下速度:

8d98c700-4217-11ee-a2ef-92fbcf53809c.png

嗯,不錯,有效果,44ms->42ms,相當于PRelu算子執(zhí)行速度從13ms->11ms; 還可以,無痛漲點。翻看arm_nn_requantize函數(shù),其中也不乏一些手撕浮點數(shù)的神秘操作??紤]到我們的RT1170本身兼?zhèn)湟粋€FPU單元,為啥不直接用浮點數(shù)計算呢?這次我們不對scale參數(shù)進行指數(shù)化轉換,而是直接將其作為浮點數(shù)參與運算,公式就是上面我們推導的:

 // init the float mul, shift
  float real_multiplier_1 = (input->params.scale) / (output->params.scale);
  float real_multiplier_2 = (input->params.scale) * (alpha->params.scale) / (output->params.scale);

計算方式重新定義為:

output_value = MultiplyByQuantizedMultiplierFP32(
                input_value, multiplier_pos);
static inline int32_t MultiplyByQuantizedMultiplierFP32(int32_t x, float mul){
  return roundf(x * mul);

是不是看著非常清爽?讓我們看下時間:

8db06e50-4217-11ee-a2ef-92fbcf53809c.png

額。。。有點尷尬,竟然沒有長點,而且和TFLm的原始實現(xiàn)速度一樣。小編才提到的內存優(yōu)化不是還沒有上?浮點運算這邊還有小插曲,讓我們繼續(xù)前行:

首先讓我們先看下浮點操作再如何進行優(yōu)化,由于我們的代碼由于采用了Ofast優(yōu)化策略,因此代碼的可閱讀性變得很差。為了進行代碼優(yōu)化,小編需要特殊編寫一組浮點運算代碼以供優(yōu)化參考,因為我們最終實現(xiàn)的是一個int32數(shù)據與浮點數(shù)相乘:

static inline int32_t MultiplyByQuantizedMultiplierFP32(int32_t x, float mul){
  return roundf(x * mul);
}

編寫代碼如下:

 int32_t v1 = (float)SysTick->VAL;
    float v2 = SysTick->VAL * 0.0001f;
    int32_t v3 = (v1 * v2);
    PRINTF("%d", v3);

其所生成的匯編代碼為:

int32_t v1 = (float)SysTick->VAL;
     800040DC   LDR            R2, [R0]
     800040DE   STRD           R2, R1, [SP]
     800040E2   VLDR           D0, [SP]
     800040E8   VSUB.F64       D0, D0, D1
     800040F0   VCVT.F32.F64   S0, D0
     800040F8   VCVT.S32.F32   S0, S0
     800040FE   VMOV           R0, S0
    float v2 = SysTick->VAL * 0.0001f;
     800040E6   LDR            R0, [R0]
     800040EC   STRD           R0, R1, [SP, #16]
     800040F4   VLDR           D2, [SP, #16]
     80004102   VSUB.F64       D0, D2, D1
     80004106   VLDR           D2, =0x4330000080000000
     80004110   VCVT.F32.F64   S0, D0
     80004122   VMUL.F32       S0, S0, S4
    int32_t v3 = (v1 * v2);
     800040FC   STR            R1, [SP, #12]
     8000410A   EOR            R0, R0, #0x80000000
     8000410E   STR            R0, [SP, #8]
     80004116   VLDR           D1, [SP, #8]
     8000411A   VSUB.F64       D1, D1, D2
     8000411E   VLDR           S4, =0x38D1B717            
     80004126   VCVT.F32.F64   S2, D1
     8000412A   VMUL.F32       S0, S2, S0

到這里,小伙伴們可能已經看到了端倪,小編也特意為大家標紅了幾條匯編代碼。那小編就先拋出疑問:我們明明定義的浮點型, 咋還用上double類型了呢?相同的代碼用GCC編譯會是什么樣的呢?

int32_t v1 = (float)SysTick->VAL;
300030f2:   mov.w   r3, #3758153728 ; 0xe000e000
300030f6:   vldr    s15, [r3, #24]
71            float v2 = SysTick->VAL * 0.0001f;
300030fa:   vldr    s14, [r3, #24]
300030fe:   vcvt.f32.u32    s14, s14
30003102:   vldr    s13, [pc, #92]  ; 0x30003160 +148>
30003106:   vmul.f32        s14, s14, s13
72            int32_t v3 = __builtin_roundf(v1 * v2);
3000310a:   vcvt.f32.s32    s15, s15
3000310e:   vmul.f32        s15, s15, s14
30003112:   vrinta.f32      s15, s15

看似正常,沒有使用double類型寄存器;那問題出在哪呢?難道Keil對于浮點數(shù)的支持不太行?翻閱了一萬件資料之后,小編在編譯時使用一個叫做-ffp-mode = full的參數(shù),這個參數(shù)的意思是:

8dcecf26-4217-11ee-a2ef-92fbcf53809c.png

同時還有兩個參數(shù),是-fp-mode=fast和-fp-mode=std,簡單來講就是full會保證轉換精度,因此會出現(xiàn)使用double類型的情況。而fast可能會丟失一點精度,而std介于兩者之間。那么我們定義-fp-mode=std試試?

8df75bc6-4217-11ee-a2ef-92fbcf53809c.png

代碼如下:

int32_t v1 = (float)SysTick->VAL;
     800040D4   VLDR           S0, [R0]
     800040E2   VCVT.F32.U32   S0, S0
    float v2 = SysTick->VAL * 0.0001f;
     800040D8   VLDR           S2, [R0]
     800040DC   VCVT.F32.U32   S2, S2
     800040E6   VMUL.F32       S2, S2, S4
    int32_t v3 = (v1 * v2);
     800040EA   VRINTZ.F32     S0, S0
     800040EE   VMUL.F32       S0, S2, S0

嗯,優(yōu)雅,就是這么簡單。指令條數(shù)減少了很多啊,讓我們再來看看時間:

8e058d04-4217-11ee-a2ef-92fbcf53809c.png

這樣一來就和arm提供的方式一致了,相比實現(xiàn)就清爽了很多。

接下來小編還有一個殺手锏,內存優(yōu)化,不過此處的內存優(yōu)化是有個前提,我們知道PRelu的alpha參數(shù)是按通道的,這里要做個特殊的假設,假設輸入維度為 h w c,而且alpha參數(shù)是按h w共享的,即只有最后一維參數(shù),維度為11 c:

if((alpha_shape.Dims(0) == 1) && (alpha_shape.Dims(1) == 1))

這樣我們就可以按c通道進行展開,并進行順序訪問;

其次,輸入數(shù)據為int8類型,原始實現(xiàn)方式中每次只取一個數(shù)據進行計算:

const int32_t input_value =
              params.input_offset + input_data[input_index];

這樣編譯器會將起編譯為LDRB指令,即每次只獲取一個字節(jié)的數(shù)據。對此進行優(yōu)化,每次讀取4個字節(jié)的數(shù)據,這樣可以編譯為LDR指令,并放置于寄存器中,減少訪存次數(shù):

uint32_t steps = alpha_shape.Dims(2);
uint32_t total_size = input_shape.Dims(0) * input_shape.Dims(1) * input_shape.Dims(2) * input_shape.Dims(3);
for(int value_index=0;value_index    T *alpha = (T *)alpha_data;
    // each 4, calc the time_tick
    uint32_t inner_loop = steps >> 2;
    int8_t *input_data_ptr = (int8_t*)input_data + value_index;
    int8_t *output_data_ptr = (int8_t*)output_data + value_index;
    while(inner_loop --){
       int32_t input_data_32 = *((int32_t*)(input_data_ptr));
       input_data_ptr += 4;
       uint32_t count = 4;
          while(count--){
              int8_t input_data_8 = input_data_32 & 0xFF;
              input_data_32 >>= 8;
       。。。。

;value_index+=steps){>

這樣一來,就可以順序取數(shù)據,并且每次讀取4個字節(jié),看下時間:

8e32c1d4-4217-11ee-a2ef-92fbcf53809c.png

Nice!~

PRelu的時間變?yōu)?7ms – 31ms = 6ms。經過兩步優(yōu)化,將PRelu的執(zhí)行時間降低了7ms。用客戶的模型測試一下,PRelu算子運行時間從之前的188ms降低到了51ms。Perfect!

不過,小編精益求精,還有一些微小的優(yōu)化空間,后續(xù)將會進一步優(yōu)化。

歡迎朋友們持續(xù)關注~


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • mcu
    mcu
    +關注

    關注

    147

    文章

    19165

    瀏覽量

    404888
  • NXP
    NXP
    +關注

    關注

    61

    文章

    1411

    瀏覽量

    199463
  • 恩智浦
    +關注

    關注

    14

    文章

    6130

    瀏覽量

    155252
  • 函數(shù)
    +關注

    關注

    3

    文章

    4423

    瀏覽量

    67884
  • 算子
    +關注

    關注

    0

    文章

    16

    瀏覽量

    7415

原文標題:PRelu算子調優(yōu)經歷-函數(shù)優(yōu)化策略

文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    Nginx高并發(fā)連接調優(yōu)實戰(zhàn)手冊

    Nginx 的高性能源自其事件驅動架構。與 Apache 的"每連接一線程"模型不同,Nginx 使用單線程事件循環(huán)處理數(shù)千個并發(fā)連接。理解這套架構是調優(yōu)的前提。
    的頭像 發(fā)表于 03-16 15:28 ?500次閱讀

    解鎖Zephyr實時操作系統(tǒng)深度調優(yōu)能力

    可以說,代碼編寫只是項目開發(fā)的起點,而隨之而來的資源分析與性能調優(yōu)才是確保系統(tǒng)穩(wěn)定可靠的關鍵環(huán)節(jié)。
    的頭像 發(fā)表于 01-30 09:16 ?6321次閱讀

    Linux系統(tǒng)內核參數(shù)調優(yōu)實戰(zhàn)指南

    Linux 內核參數(shù)調優(yōu)是系統(tǒng)性能優(yōu)化的核心環(huán)節(jié)。隨著云原生架構的普及和硬件性能的飛速提升,默認的內核參數(shù)配置往往無法充分發(fā)揮系統(tǒng)潛力。在高并發(fā) Web 服務、大數(shù)據處理、容器化部署等場景下,合理的內核參數(shù)調整可帶來 30%-2
    的頭像 發(fā)表于 01-28 14:27 ?774次閱讀

    性能測試調優(yōu)實戰(zhàn)與探索(存儲模型優(yōu)化+調用鏈路分析)

    分析、流量分析、壓測實施和剖解調優(yōu)等主要環(huán)節(jié)中,引發(fā)對于系統(tǒng)能力底盤夯實和測試策略改進的諸多思考。 在性能測試階段,剖析系統(tǒng)能力實現(xiàn)及調優(yōu)方案,探索更優(yōu)解及性能測試策略的提升空間。 ?
    的頭像 發(fā)表于 01-12 14:46 ?2920次閱讀
    性能測試<b class='flag-5'>調</b><b class='flag-5'>優(yōu)</b>實戰(zhàn)與探索(存儲模型<b class='flag-5'>優(yōu)化</b>+調用鏈路分析)

    調函數(shù)例子的應用

    ; } 2、 回調函數(shù)實例(很有用) 一個GPRS模塊聯(lián)網的小項目,使用過的同學大概知道2G、4G、NB等模塊要想實現(xiàn)無線聯(lián)網功能都需要經歷模塊上電初始化、注冊網絡、查詢網絡信息質量、連接服務器等
    發(fā)表于 12-11 07:23

    HarmonyOSAI編程智慧調優(yōu)

    DevEco Studio提供智慧調優(yōu)能力,支持通過自然語言交互,分析并解釋當前實例或項目中存在的性能問題,幫助開發(fā)者快速定位影響性能的具體原因。該功能從DevEco Studio 6.0.0
    發(fā)表于 09-01 15:15

    大彩講堂:VisualHMI-LUA教程-on_screen_change回調函數(shù)使用指南

    on_screen_change回調函數(shù)使用指南
    的頭像 發(fā)表于 08-31 16:32 ?1210次閱讀
    大彩講堂:VisualHMI-LUA教程-on_screen_change回<b class='flag-5'>調</b><b class='flag-5'>函數(shù)</b>使用指南

    大彩講堂:VisualHMI-LUA教程-on_update回調函數(shù)使用指南

    調函數(shù)使用指南
    的頭像 發(fā)表于 08-31 16:25 ?1125次閱讀
    大彩講堂:VisualHMI-LUA教程-on_update回<b class='flag-5'>調</b><b class='flag-5'>函數(shù)</b>使用指南

    HarmonyOS AI輔助編程工具(CodeGenie)智慧調優(yōu)

    DevEco Studio提供智慧調優(yōu)能力,支持通過自然語言交互,分析并解釋當前實例或項目中存在的性能問題,幫助開發(fā)者快速定位影響性能的具體原因。該功能從DevEco Studio 6.0.0
    發(fā)表于 08-14 11:12

    Linux網絡性能調優(yōu)方案

    在當今高并發(fā)、大流量的互聯(lián)網環(huán)境下,網絡性能往往成為系統(tǒng)的瓶頸。作為一名資深運維工程師,我在生產環(huán)境中遇到過無數(shù)次因為TCP/IP參數(shù)配置不當導致的性能問題。今天分享一套完整的Linux網絡性能調優(yōu)方案,幫助大家徹底解決網絡性能瓶頸。
    的頭像 發(fā)表于 08-06 18:01 ?1529次閱讀

    Linux內核參數(shù)調優(yōu)方案

    在高并發(fā)微服務環(huán)境中,網絡性能往往成為K8s集群的瓶頸。本文將深入探討如何通過精細化的Linux內核參數(shù)調優(yōu),讓你的K8s節(jié)點網絡性能提升30%以上。
    的頭像 發(fā)表于 08-06 17:50 ?1195次閱讀

    Linux系統(tǒng)性能調優(yōu)方案

    關鍵要點預覽:本文將深入解析Linux系統(tǒng)性能瓶頸的根本原因,提供可直接落地的調優(yōu)方案,讓你的系統(tǒng)性能提升30-50%!
    的頭像 發(fā)表于 08-06 17:49 ?1102次閱讀

    MySQL配置調優(yōu)技巧

    上個月,我們公司的核心業(yè)務系統(tǒng)突然出現(xiàn)大面積超時,用戶投訴電話不斷。經過緊急排查,發(fā)現(xiàn)是MySQL服務器CPU飆升到99%,大量慢查詢堆積。通過一系列配置調優(yōu)和SQL優(yōu)化,最終在30分鐘內恢復了服務。
    的頭像 發(fā)表于 07-31 10:27 ?836次閱讀

    Nginx在企業(yè)環(huán)境中的調優(yōu)策略

    Nginx作為現(xiàn)代互聯(lián)網架構中最重要的Web服務器和反向代理服務器,其性能調優(yōu)對企業(yè)級應用的穩(wěn)定性和效率至關重要。本指南將從運維實踐角度出發(fā),詳細介紹Nginx在企業(yè)環(huán)境中的各種調優(yōu)
    的頭像 發(fā)表于 07-14 11:13 ?822次閱讀

    手把手教你如何調優(yōu)Linux網絡參數(shù)

    在高并發(fā)網絡服務場景中,Linux內核的默認網絡參數(shù)往往無法滿足需求,導致性能瓶頸、連接超時甚至服務崩潰。本文基于真實案例分析,從參數(shù)解讀、問題診斷到優(yōu)化實踐,手把手教你如何調優(yōu)Linux網絡參數(shù),支撐百萬級并發(fā)連接。
    的頭像 發(fā)表于 05-29 09:21 ?1226次閱讀
    克拉玛依市| 邹城市| 克东县| 睢宁县| 无棣县| 宝清县| 平原县| 黄浦区| 常州市| 布拖县| 临沭县| 天门市| 临江市| 渭源县| 辽阳市| 建始县| 大姚县| 合肥市| 那曲县| 井陉县| 灵川县| 昔阳县| 石狮市| 万州区| 洛浦县| 襄汾县| 永修县| 武穴市| 乐陵市| 望江县| 靖边县| 襄垣县| 康平县| 承德市| 太仆寺旗| 永新县| 喀什市| 邵武市| 临泽县| 汝州市| 武强县|