chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

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

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

移動(dòng)端arm cpu優(yōu)化學(xué)習(xí)筆記(一)一步步優(yōu)化盒子濾波

電子設(shè)計(jì) ? 來源:電子設(shè)計(jì) ? 作者:電子設(shè)計(jì) ? 2020-12-10 20:23 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

盒子濾波算是很基礎(chǔ)和經(jīng)典的函數(shù),但是在PC上實(shí)現(xiàn)的話因?yàn)橛?a href="http://www.brongaenegriffin.com/tags/gpu/" target="_blank">GPU,借助其強(qiáng)大的算力所以可以很暴力的實(shí)現(xiàn),每個(gè)thread計(jì)算以某點(diǎn)為中心給定半徑下的區(qū)域大小的和即可。那如果在移動(dòng)端cpu上如何寫高效的盒子濾波操作呢?
作者:梁德澎

最近一段時(shí)間做比較多移動(dòng)端開發(fā)相關(guān)的工作,感覺移動(dòng)端優(yōu)化相關(guān)的對(duì)我來說挺有趣的,以前都是在PC上寫代碼,寫代碼的時(shí)候?qū)τ诖a的性能沒有過多的思考和感覺。但是在移動(dòng)端上寫代碼明顯能察覺到一段代碼寫的好不好,對(duì)于在移動(dòng)端上運(yùn)行性能有很大的影響,尤其在一些比較老舊的機(jī)型上測(cè)試更能有感覺。

然后最近剛好在復(fù)現(xiàn)一篇論文,要在MXNet中實(shí)現(xiàn)類似盒子濾波(box filter)的操作子,其實(shí)就是步長為1的sum pooling,盒子濾波算是很基礎(chǔ)和經(jīng)典的函數(shù),但是在PC上實(shí)現(xiàn)的話因?yàn)橛蠫PU,借助其強(qiáng)大的算力所以可以很暴力的實(shí)現(xiàn),每個(gè)thread計(jì)算以某點(diǎn)為中心給定半徑下的區(qū)域大小的和即可。然后突發(fā)奇想想試試在移動(dòng)端cpu上試試如何寫高效的盒子濾波操作。

這篇文章就是把我的實(shí)踐過程記錄下來,首先給出最簡(jiǎn)單的實(shí)現(xiàn)然后如何一步步優(yōu)化,到最后給出一個(gè)性能優(yōu)化還不錯(cuò)的版本。由于我正式接觸移動(dòng)端優(yōu)化的時(shí)間不長,很多東西理解的不深,所以有哪里論述不正確的地方請(qǐng)讀者指出。

本文的代碼:
https://github.com/Ldpe2G/ArmNeonOptimization/tree/master/boxFilter

1.首先來看下Boxfilter最簡(jiǎn)單最直觀的實(shí)現(xiàn)

void BoxFilter::filter(float *input, int radius, int height, int width, float *output) {
  for (int h = 0; h < height; ++h) {
    int height_sift = h * width;
    for (int w = 0; w < width; ++w) {
      int start_h = std::max(0, h - radius);
      int end_h = std::min(height - 1, h + radius);
      int start_w = std::max(0, w - radius);
      int end_w = std::min(width - 1, w + radius);

      float tmp = 0;
      for (int sh = start_h; sh <= end_h; ++sh) {
        for (int sw = start_w; sw <= end_w; ++ sw) {
          tmp += input[sh * width + sw];
        }
      }
      output[height_sift + w] = tmp;
    }
  }
}

對(duì)每個(gè)點(diǎn),計(jì)算給定半徑下區(qū)域的和,需要注意下邊界的處理。

其時(shí)間復(fù)雜度是 O( height x width x (radius x 2 + 1) x (radius x 2 + 1) ),

這個(gè)最簡(jiǎn)單的實(shí)現(xiàn)在輸入大小固定的情況下,半徑越大耗時(shí)越大,有很多重復(fù)計(jì)算的地方,相鄰元素在計(jì)算各自區(qū)域內(nèi)和的時(shí)候其實(shí)是有重疊的。然后第一個(gè)優(yōu)化的思路就是boxfilter的計(jì)算是行列可分離的,具體可參考[4]。

2.Boxfilter優(yōu)化第一版

void BoxFilter::fastFilter(float *input, int radius, int height, int width, float *output) {
  float *cachePtr = &(cache[0]);
  // sum horizonal
  for (int h = 0; h < height; ++h) {
    int sift = h * width;
    for (int w = 0; w < width; ++w) {
      int start_w = std::max(0, w - radius);
      int end_w = std::min(width - 1, w + radius);

      float tmp = 0;
      for (int sw = start_w; sw <= end_w; ++ sw) {
        tmp += input[sift + sw];
      }

      cachePtr[sift + w] = tmp;
    }
  }

  // sum vertical
  for (int h = 0; h < height; ++h) {
    int shift = h * width;
    int start_h = std::max(0, h - radius);
    int end_h = std::min(height - 1, h + radius);

    for (int sh = start_h; sh <= end_h; ++sh) {
      int out_shift = sh * width;
      for (int w = 0; w < width; ++w) {
        output[out_shift + w] += cachePtr[shift + w];
      }
    }
  }
}

所謂行列可分離就是,把行列分開計(jì)算,從代碼里可以看到,對(duì)每個(gè)元素,首先計(jì)算行方向上半徑內(nèi)的和,然后再計(jì)算列半徑內(nèi)的和,所以這時(shí)候的時(shí)間復(fù)雜度是O(height x width x (radius x 2 + 1) x 2)。

可以看到行列分離之后,時(shí)間復(fù)雜度減少了不少,尤其半徑越大減少的越多,但是還是有重復(fù)計(jì)算的地方。而且在固定輸入下時(shí)間復(fù)雜度還是會(huì)隨半徑的變大而變大。那么有沒有方法可以使得計(jì)算復(fù)雜度不受半徑的影響呢?優(yōu)化思路就是比如在算某一行每個(gè)點(diǎn)的半徑區(qū)域內(nèi)的和時(shí),對(duì)于行開頭第一個(gè)點(diǎn),首先計(jì)算其半徑內(nèi)和,然后對(duì)于接下來的點(diǎn),不需要重新計(jì)算其半徑區(qū)域內(nèi)和,而是只需要把前一個(gè)元素半徑內(nèi)的和,按半徑窗口偏移之后減去舊的點(diǎn)和加上新加入的點(diǎn)即可。

3.Boxfilter優(yōu)化第二版

void BoxFilter::fastFilterV2(float *input, int radius, int height, int width, float *output) {
  float *cachePtr = &(cache[0]);
  // sum horizonal
  for (int h = 0; h < height; ++h) {
    int shift = h * width;

    float tmp = 0;
    for (int w = 0; w < radius; ++w) {
      tmp += input[shift + w];
    }

    for (int w = 0; w <= radius; ++w) {
      tmp += input[shift + w + radius];
      cachePtr[shift + w] = tmp;
    }

    int start = radius + 1;
    int end = width - 1 - radius;
    for (int w = start; w <= end; ++w) {
      tmp += input[shift + w + radius];
      tmp -= input[shift + w - radius - 1];
      cachePtr[shift + w] = tmp;
    }

    start = width - radius;
    for (int w = start; w < width; ++w) {
      tmp -= input[shift + w - radius - 1];
      cachePtr[shift + w] = tmp;
    }
  }

  float *colSumPtr = &(colSum[0]);
  for (int indexW = 0; indexW < width; ++indexW) {
    colSumPtr[indexW] = 0;
  } 
  // sum vertical
  for (int h = 0; h < radius; ++h) {
    int shift = h * width;
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] += cachePtr[shift + w];
    }
  }

  for (int h = 0; h <= radius; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] += addPtr[w];
      outPtr[w] = colSumPtr[w];
    }
  }

  int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift;
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] += addPtr[w];
      colSumPtr[w] -= subPtr[w];
      outPtr[w] = colSumPtr[w];
    }
  }

  start = height - radius;
  for (int h = start; h < height; ++h) {
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] -= subPtr[w];
      outPtr[w] = colSumPtr[w];
    }
  }
}

這一版時(shí)間復(fù)雜度大概是O(height x width x 4 )。不算邊界只看中間部分的計(jì)算就是一次加法和一次減法,行方向和列方向都一樣。這里行方向的部分很好理解,因?yàn)檫吔绮糠中枰厥馓幚?,比如開始部分只有加,結(jié)尾部分只有減法,所以計(jì)算分成了3部分。列方向計(jì)算的話按照常規(guī)思路,那就是按一列列來處理,可是我們知道數(shù)據(jù)一般是按照行來存儲(chǔ)的,這樣子跳行取數(shù)據(jù),會(huì)造成很多次cache miss,這樣子性能肯定會(huì)受很大的影響,所以這里用了一個(gè)大小是width的向量colSum,來存儲(chǔ)每一列對(duì)應(yīng)點(diǎn)的半徑區(qū)域內(nèi)的和,然后遍歷的時(shí)候還是按照行來遍歷,如果一下子理解不了這個(gè)思路的話,可以想象如果width為1的情況,那么應(yīng)該可以更好的理解。

然后我們來看下實(shí)驗(yàn)結(jié)果,這三版boxfilter在輸入是2000x2000的情況下,在不同半徑下的運(yùn)行耗時(shí),測(cè)試手機(jī)是華為榮耀4C(CHM-TL00),每個(gè)函數(shù)運(yùn)行10次取平均為其耗時(shí):

可以看到第二版優(yōu)化的耗時(shí)在不同半徑下的表現(xiàn)都很穩(wěn)定,基本不受影響。然后接下來的優(yōu)化思路就是在確定了C++ 的代碼之后可以采用arm Neon Intrinsics來加速了,就是利用向量計(jì)算指令同時(shí)處理多個(gè)數(shù)據(jù),把獨(dú)立的運(yùn)算同時(shí)做,比寫匯編要容易。

4.Boxfilter優(yōu)化第二版 Neon Intrinsics

int n = width >> 2;
  int re = width - (n << 2);

  int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    int indexW = 0;
    float *tmpOutPtr = outPtr;
    float *tmpColSumPtr = colSumPtr;
    float *tmpAddPtr = addPtr;
    float *tmpSubPtr = subPtr;

    int nn = n;
    int remain = re;
#if __ARM_NEON
    for (; nn > 0; nn--) {
      float32x4_t _add = vld1q_f32(tmpAddPtr);
      float32x4_t _sub = vld1q_f32(tmpSubPtr);
      float32x4_t _colSum = vld1q_f32(tmpColSumPtr);

      float32x4_t _tmp = vaddq_f32(_colSum, _add);
      _tmp = vsubq_f32(_tmp, _sub);

      vst1q_f32(tmpColSumPtr, _tmp);
      vst1q_f32(tmpOutPtr, _tmp);

      tmpAddPtr += 4;
      tmpSubPtr += 4;
      tmpColSumPtr += 4;
      tmpOutPtr += 4;
    }
#endif // __ARM_NEON
    for (; remain > 0; --remain) {
      *tmpColSumPtr += *tmpAddPtr;
      *tmpColSumPtr -= *tmpSubPtr;
      *tmpOutPtr = *tmpColSumPtr;
      tmpAddPtr ++;
      tmpColSumPtr ++;
      tmpOutPtr ++;
      tmpSubPtr ++;
    }
  }

上面的代碼是截取列方向中間計(jì)算部分來展示如何使用arm Neon Intrinsics函數(shù),完整代碼可以看
https://github.com/Ldpe2G/ArmNeonOptimization/blob/master/boxFilter/src/boxFilter.cpp#L143
行方向是沒辦法并行的,因?yàn)橄噜徳赜幸蕾?。而列方向上則可以,所以在列方向上做neon加速。
以上代碼其實(shí)挺好理解的,vld1q/_f32指令就是加載4個(gè)浮點(diǎn)數(shù),然后vaddq/_f32,為把兩個(gè)float32x4/_t向量相加,相當(dāng)于同時(shí)計(jì)算了4個(gè)輸出,然后再把結(jié)果用vst1q/_f32存回去對(duì)應(yīng)的地址,然后所有參與運(yùn)算的地址都是每次加4,具體可以參考官網(wǎng)文檔。

然后來看下這版優(yōu)化的耗時(shí)如何:

可以看到耗時(shí)又少了一點(diǎn),但是收益已經(jīng)不大了。然后還想嘗試進(jìn)一步優(yōu)化把Intrinsics部分改寫成內(nèi)聯(lián)匯編試試。

5.Boxfilter優(yōu)化第二版 Neon Assembly

int n = width >> 2;
  int re = width - (n << 2);

  int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    int indexW = 0;
    float *tmpOutPtr = outPtr;
    float *tmpColSumPtr = colSumPtr;
    float *tmpAddPtr = addPtr;
    float *tmpSubPtr = subPtr;

    int nn = n;
    int remain = re;
#if __ARM_NEON
    asm volatile(
      "0:                       /n"
      "vld1.s32 {d0-d1}, [%0]!  /n"
      "vld1.s32 {d2-d3}, [%1]!  /n"
      "vld1.s32 {d4-d5}, [%2]   /n"
      "vadd.f32 q4, q0, q2      /n"
      "vsub.f32 q3, q4, q1      /n"
      "vst1.s32 {d6-d7}, [%3]!  /n"
      "vst1.s32 {d6-d7}, [%2]!  /n"
      "subs %4, #1              /n"
      "bne  0b                  /n"
      : "=r"(tmpAddPtr), //
      "=r"(tmpSubPtr),
      "=r"(tmpColSumPtr),
      "=r"(tmpOutPtr),
      "=r"(nn)
      : "0"(tmpAddPtr),
      "1"(tmpSubPtr),
      "2"(tmpColSumPtr),
      "3"(tmpOutPtr),
      "4"(nn)
      : "cc", "memory", "q0", "q1", "q2", "q3", "q4"
    );

#endif // __ARM_NEON
    for (; remain > 0; --remain) {
      *tmpColSumPtr += *tmpAddPtr;
      *tmpColSumPtr -= *tmpSubPtr;
      *tmpOutPtr = *tmpColSumPtr;
      tmpAddPtr ++;
      tmpColSumPtr ++;
      tmpOutPtr ++;
      tmpSubPtr ++;
    }
  }

完整版代碼:https://github.com/Ldpe2G/ArmNeonOptimization/blob/master/boxFilter/src/boxFilter.cpp#L331

這里我只對(duì)列計(jì)算中間部分做了改寫,neon匯編下面的"cc","memory"之后跟的寄存器,是為了告訴編譯器(主要是q開頭的,q和d是一樣的,q表示128位向量寄存器(16個(gè)),d表示64位(32個(gè)),q0 =(d0 + d1)),這些寄存器會(huì)在匯編內(nèi)被用到,然后編譯器在進(jìn)入這段代碼之前,要緩存這些寄存器的內(nèi)容,然后在離開這段匯編之后恢復(fù)原來的值。一定要記得寫上用了哪些向量寄存器。

簡(jiǎn)單解釋一下,指令的意思,"vld1.s32 {d0-d1}, [%0]! /n",相當(dāng)?shù)扔趶膖mpAddPtr這個(gè)地址連續(xù)讀取4個(gè)浮點(diǎn)數(shù)到{d0-d1}也就是q0寄存器,浮點(diǎn)數(shù)每個(gè)32位,乘以四就是128位。最后的感嘆號(hào)表示,這個(gè)指令完成之后tmpAddPtr地址加4的意思,沒有就是不變。"vadd.f32 q4, q0, q2 /n" 就是把 q0和q2相加的結(jié)果放到q4,"vsub.f32 q3, q4, q1 /n" 就是把q4減去q1的結(jié)果放到q3,和上面的intrinsics指令對(duì)應(yīng)。
然后vst1.s32就是把寄存器的內(nèi)容存到tmpOutPtr和tmpColSumPtr地址指向的內(nèi)存。
最后的subs指令和bne相當(dāng)于for循環(huán)的功能,最后對(duì)nn減一然后bne判斷是否為0, 不為0則繼續(xù)循環(huán)跳到開頭0標(biāo)記出繼續(xù)執(zhí)行。

匯編指令其實(shí)和intrinsics函數(shù)有對(duì)應(yīng)的具體可參考官方文檔。

然后我們來看下耗時(shí):

什么鬼,竟然還慢了,一定是我使用的方式不對(duì)。去查了下資料,看到這篇博客里面提到,指令vld和vst都是需要消耗兩個(gè)時(shí)鐘周期,其他指令基本都是一個(gè)時(shí)鐘周期,但是卻不意味著一個(gè)時(shí)鐘周期之后能立刻得到結(jié)果。那么看下來 vsub.f32 指令依賴 vadd.f32 的結(jié)果,所以白白浪費(fèi)了不少時(shí)鐘周期。而且現(xiàn)代的處理器支持雙發(fā)射流水線,也就意味著CPU可以同時(shí)拾取兩條數(shù)據(jù)無關(guān)指令,那么能否利用這點(diǎn)來更進(jìn)一步加速呢。

6.Boxfilter優(yōu)化第二版 Neon Assembly 第二版

int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    int indexW = 0;
    float *tmpOutPtr = outPtr;
    float *tmpColSumPtr = colSumPtr;
    float *tmpAddPtr = addPtr;
    float *tmpSubPtr = subPtr;

    int nn = width >> 3;
    int remain = width - (nn << 3);
#if __ARM_NEON
    asm volatile(
      "0:                       /n"
      "pld      [%0, #256]      /n"
      "vld1.s32 {d0-d3}, [%0]!  /n"
      "pld      [%2, #256]      /n"
      "vld1.s32 {d8-d11}, [%2]  /n"

      "vadd.f32 q6, q0, q4      /n"

      "pld      [%1, #256]      /n"
      "vld1.s32 {d4-d7}, [%1]!  /n"

      "vadd.f32 q7, q1, q5      /n"

      "vsub.f32 q6, q6, q2      /n"

      "vsub.f32 q7, q7, q3      /n"

      "vst1.s32 {d12-d15}, [%3]!  /n"

      // 感謝 @隨風(fēng)漂 指出這里錯(cuò)誤,用錯(cuò)了寄存器,輸出結(jié)果是錯(cuò)的
      // "vst1.s32 {d16-d19}, [%2]!  /n" 

      "vst1.s32 {d12-d15}, [%2]!  /n"

      "subs %4, #1              /n"
      "bne  0b                  /n"
      : "=r"(tmpAddPtr), //
      "=r"(tmpSubPtr),
      "=r"(tmpColSumPtr),
      "=r"(tmpOutPtr),
      "=r"(nn)
      : "0"(tmpAddPtr),
      "1"(tmpSubPtr),
      "2"(tmpColSumPtr),
      "3"(tmpOutPtr),
      "4"(nn)
      : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9"
    );

#endif // __ARM_NEON
    for (; remain > 0; --remain) {
      *tmpColSumPtr += *tmpAddPtr;
      *tmpColSumPtr -= *tmpSubPtr;
      *tmpOutPtr = *tmpColSumPtr;
      tmpAddPtr ++;
      tmpColSumPtr ++;
      tmpOutPtr ++;
      tmpSubPtr ++;
    }
  }

完整版代碼:https://github.com/Ldpe2G/ArmNeonOptimization/blob/master/boxFilter/src/boxFilter.cpp#L527

可以看到這里的改進(jìn)思路就是,把兩條 vadd.f32 指令放一起,然后跟兩條vsub.f32,然后把加載 vsub.f32 要用到部分?jǐn)?shù)據(jù)指令 vld1.s32 放到兩個(gè) vadd.f32之間,同時(shí) vld1.s32 指令之前加上 pld 指令。這個(gè)指令為什么能加速我問了下做移動(dòng)端優(yōu)化的同事,pld把數(shù)據(jù)從內(nèi)存加載到cache然后下一條指令把數(shù)據(jù)從 cache加載到寄存器,如果不用pld,數(shù)據(jù)若不在cache中,那么就是需要直接從內(nèi)存加載到寄存器,這里會(huì)比前者慢很多。

然后我們來看下最終版的耗時(shí):

看表格最終版的耗時(shí)比起最原始的實(shí)現(xiàn)至少可以加速6~7倍,肯定是還有更好的優(yōu)化方式,比如如果能對(duì)輸入做量化把float類型數(shù)據(jù)轉(zhuǎn)成8bit整型,那么就可以在單位時(shí)間處理更多數(shù)據(jù),當(dāng)然量化到8bit上計(jì)算溢出的風(fēng)險(xiǎn)也會(huì)增大許多。有時(shí)候煉丹煉久了,學(xué)習(xí)下優(yōu)化也挺好玩的,感覺可以很好的鍛煉下思維和代碼能力,現(xiàn)在深度學(xué)習(xí)在移動(dòng)端應(yīng)用越來越廣泛,訓(xùn)出來的模型如果部署到移動(dòng)端之后運(yùn)行的效率很低那么也是白費(fèi)功夫。所以感覺對(duì)移動(dòng)端優(yōu)化有一定的了解對(duì)于如何設(shè)計(jì)對(duì)移動(dòng)端更友好的模型還是有幫助的。

更多AI移動(dòng)端優(yōu)化的請(qǐng)關(guān)注專欄嵌入式AI以及知乎(@梁德澎)。

審核編輯 黃昊宇
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • ARM
    ARM
    +關(guān)注

    關(guān)注

    135

    文章

    9498

    瀏覽量

    388430
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11213

    瀏覽量

    222779
  • 人工智能
    +關(guān)注

    關(guān)注

    1813

    文章

    49709

    瀏覽量

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

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    華納云香港服務(wù)器數(shù)據(jù)庫索引優(yōu)化策略

    )實(shí)施有效的索引優(yōu)化策略。無論您是IT管理員還是開發(fā)人員,了解這些技巧都能幫助您優(yōu)化數(shù)據(jù)庫效率,確保業(yè)務(wù)高效運(yùn)轉(zhuǎn)。下面,華納云(hncloud)將通過結(jié)構(gòu)化分析,一步步解析香港服務(wù)器數(shù)據(jù)庫索引
    的頭像 發(fā)表于 10-16 17:06 ?355次閱讀

    12 個(gè)關(guān)鍵節(jié)點(diǎn)!文看懂 PCBA 如何實(shí)現(xiàn)零缺陷

    想知道 PCBA 加工怎么保證質(zhì)量零缺陷?關(guān)鍵在 12 個(gè)核心管控節(jié)點(diǎn),一步步看:?
    的頭像 發(fā)表于 09-15 15:14 ?498次閱讀
    12 個(gè)關(guān)鍵節(jié)點(diǎn)!<b class='flag-5'>一</b>文看懂 PCBA 如何實(shí)現(xiàn)零缺陷

    一步步教你正確的電磁流量計(jì)安裝

    看似微小的失誤,卻會(huì)直接導(dǎo)致系統(tǒng)運(yùn)行異常。今天簡(jiǎn)單出期電磁流量計(jì)安裝“避坑”指南,幫你從根源避開這些坑。 首先了解下電磁流量計(jì),電磁流量計(jì)是應(yīng)用法拉第電磁感應(yīng)定律,根據(jù)導(dǎo)電流體通過外夾磁場(chǎng)時(shí)感生的電動(dòng)勢(shì)來
    的頭像 發(fā)表于 09-06 10:38 ?3237次閱讀
    <b class='flag-5'>一步步</b>教你正確的電磁流量計(jì)安裝

    Arm神經(jīng)超級(jí)采樣 以ML進(jìn)一步強(qiáng)化性能 實(shí)現(xiàn)卓越的移動(dòng)圖形性能

    受限的移動(dòng)設(shè)備上平衡這些目標(biāo)體驗(yàn),往往需要權(quán)衡取舍。傳統(tǒng)的優(yōu)化升級(jí)方法不夠靈活,而實(shí)時(shí)人工智能 (AI) 渲染則又依然存在復(fù)雜、耗電或依賴硬件性能等難題。 Arm 神經(jīng)超級(jí)采樣 (Arm
    的頭像 發(fā)表于 08-14 18:15 ?4603次閱讀
    <b class='flag-5'>Arm</b>神經(jīng)超級(jí)采樣 以ML進(jìn)<b class='flag-5'>一步</b>強(qiáng)化性能 實(shí)現(xiàn)卓越的<b class='flag-5'>移動(dòng)</b><b class='flag-5'>端</b>圖形性能

    淘寶API實(shí)時(shí)競(jìng)品監(jiān)控,市場(chǎng)策略快人一步!

    在當(dāng)今激烈的電商競(jìng)爭(zhēng)中,實(shí)時(shí)掌握競(jìng)品動(dòng)態(tài)是企業(yè)制勝的關(guān)鍵。淘寶作為中國最大的電商平臺(tái),其開放API為商家提供了強(qiáng)大的工具,幫助實(shí)現(xiàn)實(shí)時(shí)競(jìng)品監(jiān)控,從而優(yōu)化市場(chǎng)策略,搶占先機(jī)。本文將一步步解析如何利用
    的頭像 發(fā)表于 08-06 14:38 ?522次閱讀

    微信小程序API集成京東庫存,移動(dòng)銷量暴漲!

    。本文將探討如何通過微信小程序API集成京東庫存系統(tǒng),實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)同步,并最終推動(dòng)移動(dòng)銷量實(shí)現(xiàn)顯著增長。我們將一步步分析集成過程、核心優(yōu)勢(shì),以及實(shí)際案例中的效果。 什么是微信小程序API與京東庫存集成? 微信小程序
    的頭像 發(fā)表于 07-31 14:07 ?582次閱讀

    智駕安全,發(fā)展到哪一步了?

    智駕安全,發(fā)展到哪一步了?
    的頭像 發(fā)表于 06-10 11:28 ?524次閱讀

    Arm 公司面向移動(dòng)市場(chǎng)的 ?Arm Lumex? 深度解讀

    面向移動(dòng)市場(chǎng)的 ? Arm Lumex ? 深度解讀 ? Arm Lumex ? 是 Arm 公司面向
    的頭像 發(fā)表于 05-29 09:54 ?4036次閱讀

    解鎖樹莓派集群:一步步打造你的超級(jí)計(jì)算陣列!

    數(shù)據(jù)處理和云原生計(jì)算學(xué)習(xí)等多種任務(wù)。什么是樹莓派集群?在樹莓派集群中,每臺(tái)樹莓派都充當(dāng)集群中的個(gè)節(jié)點(diǎn),貢獻(xiàn)處理能力和內(nèi)存以分擔(dān)工作負(fù)載。這種設(shè)置利用了并行計(jì)算,即任
    的頭像 發(fā)表于 04-25 16:17 ?1889次閱讀
    解鎖樹莓派集群:<b class='flag-5'>一步步</b>打造你的超級(jí)計(jì)算陣列!

    CoT 數(shù)據(jù)集如何讓大模型學(xué)會(huì)一步一步思考?

    目前,大模型的回答路徑基本遵循 input-output 的方式,在面對(duì)復(fù)雜任務(wù)時(shí)表現(xiàn)不佳。反之,人類會(huì)遵循套有條理的思維流程,逐步推理得出正確答案。這種差異促使人們深入思考:如何才能讓大模型“智能涌現(xiàn)”,學(xué)會(huì)像人類樣“一步
    的頭像 發(fā)表于 04-24 16:51 ?1025次閱讀
    CoT 數(shù)據(jù)集如何讓大模型學(xué)會(huì)<b class='flag-5'>一步</b><b class='flag-5'>一步</b>思考?

    【迅為電子】一步步教你完成iTOP-RK3568 EDP屏幕適配

    【迅為電子】一步步教你完成iTOP-RK3568 EDP屏幕適配
    的頭像 發(fā)表于 04-23 15:08 ?1609次閱讀
    【迅為電子】<b class='flag-5'>一步步</b>教你完成iTOP-RK3568 EDP屏幕適配

    Arm KleidiCV與OpenCV集成助力移動(dòng)計(jì)算機(jī)視覺性能優(yōu)化

    等多種應(yīng)用中。然而,這些計(jì)算機(jī)視覺應(yīng)用可能很難實(shí)現(xiàn)最優(yōu)化的延遲性能和處理速度,特別是在內(nèi)存大小、電池容量和處理能力有限的移動(dòng)設(shè)備上難度更高。 而 Arm KleidiCV 便能在其中大顯身手。該開源庫利用了最新
    的頭像 發(fā)表于 02-24 10:15 ?867次閱讀

    FRED應(yīng)用:LED發(fā)光顏色優(yōu)化

    。第四個(gè)無光線追跡面用于優(yōu)化后的顏色對(duì)比。 優(yōu)化變量 優(yōu)化的第一步涉及到變量的定義,本例中,優(yōu)化3個(gè)LED光源的光功率。因?yàn)闆]有對(duì)應(yīng)的光源
    發(fā)表于 01-17 09:39

    如果需要將DDC112U設(shè)置為非連續(xù)模式工作,應(yīng)該如何一步一步正確地設(shè)置芯片?

    或狀態(tài)8,如果沒有CONV的切換發(fā)生,是否會(huì)直停留在上電時(shí)的這個(gè)狀態(tài)?如果需要將DDC112U設(shè)置為非連續(xù)模式工作,應(yīng)該如何一步一步正確地設(shè)置芯片?
    發(fā)表于 01-09 07:43

    沙子變芯片,一步步帶你走進(jìn)高科技的微觀世界

    在科技飛速發(fā)展的今天,芯片作為現(xiàn)代科技的核心元器件,其制造過程復(fù)雜且充滿挑戰(zhàn)。芯片不僅推動(dòng)了信息技術(shù)、人工智能、物聯(lián)網(wǎng)等領(lǐng)域的進(jìn)步,還成為衡量個(gè)國家科技實(shí)力的重要指標(biāo)。然而,芯片制造并非易事,從沙子到芯片的每一步都充滿了技術(shù)、資金和人才的考驗(yàn)。本文將詳細(xì)解析芯片制造的全
    的頭像 發(fā)表于 12-19 10:44 ?1063次閱讀
    沙子變芯片,<b class='flag-5'>一步步</b>帶你走進(jìn)高科技的微觀世界