1. 程式人生 > >ncnn 框架分析 openmp多核加速 快取 仿存 cache 快速矩陣乘法 單指令多資料指令SIMD

ncnn 框架分析 openmp多核加速 快取 仿存 cache 快速矩陣乘法 單指令多資料指令SIMD

ncnn 框架分析

本文github連結

在ncnn中建立新層

ncnn 下載編譯使用

參考1

參考2

1. param 和 bin 檔案分析

param

  7767517   # 檔案頭 魔數
  75 83     # 層數量  輸入輸出blob數量
            # 下面有75行
  Input            data             0 1 data 0=227 1=227 2=3
  Convolution      conv1            1 1 data conv1 0=64 1=3 2=1 3=2 4=0 5=1 6=1728
  ReLU             relu_conv1       1 1 conv1 conv1_relu_conv1 0=0.000000
  Pooling          pool1            1 1 conv1_relu_conv1 pool1 0=0 1=3 2=2 3=0 4=0
  Convolution      fire2/squeeze1x1 1 1 pool1 fire2/squeeze1x1 0=16 1=1 2=1 3=1 4=0 5=1 6=1024
  ...
  層型別            層名字   輸入blob數量 輸出blob數量  輸入blob名字 輸出blob名字   引數字典
  
  引數字典,每一層的意義不一樣:
  資料輸入層 Input            data             0 1 data 0=227 1=227 2=3   影象寬度×影象高度×通道數量
  卷積層    Convolution  ...   0=64     1=3      2=1    3=2     4=0    5=1    6=1728           
           0輸出通道數 num_output() ; 1卷積核尺寸 kernel_size();  2空洞卷積引數 dilation(); 3卷積步長 stride(); 
           4卷積填充pad_size();       5卷積偏置有無bias_term();   6卷積核引數數量 weight_blob.data_size();
                                                              C_OUT * C_in * W_h * W_w = 64*3*3*3 = 1728
  池化層    Pooling      0=0       1=3       2=2        3=0       4=0
                      0池化方式:最大值、均值、隨機     1池化核大小 kernel_size();     2池化核步長 stride(); 
                      3池化核填充 pad();   4是否為全域性池化 global_pooling();
  啟用層    ReLU       0=0.000000     下限閾值 negative_slope();
           ReLU6      0=0.000000     1=6.000000 上下限
  
  綜合示例:
  0=1 1=2.5 -23303=2,2.0,3.0
  
  陣列關鍵字 : -23300 
  -(-23303) - 23300 = 3 表示該引數在引數陣列中的index
  後面的第一個引數表示陣列元素數量,2表示包含兩個元素

各層詳細表格

// 引數讀取 程式

// 讀取字串格式的 引數檔案
int ParamDict::load_param(FILE* fp)
{
    clear();

//     0=100 1=1.250000 -23303=5,0.1,0.2,0.4,0.8,1.0

    // parse each key=value pair
    int id = 0;
    while (fscanf(fp, "%d=", &id) == 1)// 讀取 等號前面的 key=========
    {
        bool is_array = id <= -23300;
        if (is_array)
{ id = -id - 23300;// 陣列 關鍵字 -23300 得到該引數在引數陣列中的 index } // 是以 -23300 開頭表示的陣列=========== if (is_array) { int len = 0; int nscan = fscanf(fp, "%d", &len);// 後面的第一個引數表示陣列元素數量,5表示包含兩個元素 if (nscan != 1) { fprintf
(stderr, "ParamDict read array length fail\n"); return -1; } params[id].v.create(len); for (int j = 0; j < len; j++) { char vstr[16]; nscan = fscanf(fp, ",%15[^,\n ]", vstr);//按格式解析字串============ if (nscan != 1) { fprintf(stderr, "ParamDict read array element fail\n"); return -1; } bool is_float = vstr_is_float(vstr);// 檢查該欄位是否為 浮點數的字串 if (is_float) { float* ptr = params[id].v; nscan = sscanf(vstr, "%f", &ptr[j]);// 轉換成浮點數後存入引數字典中 } else { int* ptr = params[id].v; nscan = sscanf(vstr, "%d", &ptr[j]);// 轉換成 整數後 存入字典中 } if (nscan != 1) { fprintf(stderr, "ParamDict parse array element fail\n"); return -1; } } } // 普通關鍵字========================= else { char vstr[16]; int nscan = fscanf(fp, "%15s", vstr);// 獲取等號後面的 字串 if (nscan != 1) { fprintf(stderr, "ParamDict read value fail\n"); return -1; } bool is_float = vstr_is_float(vstr);// 判斷是否為浮點數 if (is_float) nscan = sscanf(vstr, "%f", &params[id].f); // 讀入為浮點數 else nscan = sscanf(vstr, "%d", &params[id].i);// 讀入為整數 if (nscan != 1) { fprintf(stderr, "ParamDict parse value fail\n"); return -1; } } params[id].loaded = 1;// 設定該 引數以及載入 } return 0; } // 讀取 二進位制格式的 引數檔案=================== int ParamDict::load_param_bin(FILE* fp) { clear(); // binary 0 // binary 100 // binary 1 // binary 1.250000 // binary 3 | array_bit // binary 5 // binary 0.1 // binary 0.2 // binary 0.4 // binary 0.8 // binary 1.0 // binary -233(EOP) int id = 0; fread(&id, sizeof(int), 1, fp);// 讀入一個整數長度的 index while (id != -233)// 結尾 { bool is_array = id <= -23300; if (is_array) { id = -id - 23300;// 陣列關鍵字對應的 index } // 是陣列資料======= if (is_array) { int len = 0; fread(&len, sizeof(int), 1, fp);// 陣列元素數量 params[id].v.create(len); float* ptr = params[id].v; fread(ptr, sizeof(float), len, fp);// 按浮點數長度*陣列長度 讀取每一個數組元素==== } // 是普通資料======= else { fread(&params[id].f, sizeof(float), 1, fp);// 按浮點數長度讀取 該普通欄位對應的元素 } params[id].loaded = 1; fread(&id, sizeof(int), 1, fp);// 讀取 下一個 index } return 0; }

bin

    +---------+---------+---------+---------+---------+---------+
    | weight1 | weight2 | weight3 | weight4 | ....... | weightN |
    +---------+---------+---------+---------+---------+---------+
    ^         ^         ^         ^
    0x0      0x80      0x140     0x1C0

  所有權重資料連線起來, 每個權重佔 32bit

  權重資料 weight buffer

  [flag] (optional 可選)
  [raw data]
  [padding] (optional 可選)

      flag : unsigned int, little-endian, indicating the weight storage type, 
             0 => float32, 
             0x01306B47 => float16, 
             otherwise => quantized int8, 
                  may be omitted if the layer implementation forced the storage type explicitly。
      raw data : raw weight data, little-endian, 
                 float32 data or float16 data or quantized table 
                 and indexes depending on the storage type flag。
      padding : padding space for 32bit alignment, may be omitted if already aligned。

2. 輕模式

  開啟輕模式省記憶體 set_light_mode(true)
  
  每個layer都會產生blob,除了最後的結果和多分支中間結果,大部分blob都不值得保留,
  開啟輕模式可以在運算後自動回收,省下記憶體。
  
  舉個例子:某網路結構為 A -> B -> C,在輕模式下,向ncnn索要C結果時,A結果會在運算B時自動回收,
  而B結果會在運算C時自動回收,最後只保留C結果,後面再需要C結果會直接獲得,滿足絕大部分深度網路的使用方式。

3. 網路和運算是分開的

  ncnn的net是網路模型,實際使用的是extractor,
  也就是同個net可以有很多個運算例項,而且運算例項互不影響,中間結果保留在extractor內部,
  在多執行緒使用時共用網路的結構和引數資料,初始化網路模型和引數只需要一遍.
  
  舉個例子:全域性靜態的net例項,初始化一次後,就能不停地生成extractor使用.

4. openmp多核加速

// 使用OpenMP加速只需要在序列程式碼中新增編譯指令以及少量API即可。

// 如下是一個向量相加的函式(序列):

void add(const int* a, const int* b, int* c, const int len)
{
  for(int i=0; i<len; i++)
  {
    c[i] = a[i] + b[i];
  }
}

改成OpenMP多核加速(並行):

#pragma omp parallel for 
// #pragma omp parallel for  num_threads(opt.num_threads);
void add(const int* a, const int* b, int* c, const int len)
{
  for(int i=0; i<len; i++)
  {
    c[i] = a[i] + b[i];
  }
}

// 理想情況下,加速比大約能達到0.75*cores。

5. 快取,仿存,cache

快取對於高速計算是非常重要的一環,通過合理的安排記憶體讀寫,能非常有效的加速計算。
資料儲存一般是行優先儲存,而cpu仿存,是會一次多讀取當前記憶體後的連續幾個地址中的資料。

如下面程式碼的gemm計算:

版本1:

static void gemm_v1(float* matA, float* matB, float* matC, const int M, const int N, const int K, const int strideA, const int strideB, const int strideC)
{
	for (int i = 0; i < N; i++)// 外層迴圈 為 列大小,會造成過多的仿存浪費!!!!
	{
		for (int j = 0; j < M; j++)
		{
			float sum = 0.0f;
			for (int k = 0; k < K; k++)
			{
				sum += matA[j*strideA + k] * matB[k*strideB + i];// 矩陣B 是列 訪問
			}
			matC[j*strideC + i] = sum;// 矩陣C也是列 訪問(j先變換,列方向)
		}
	}
}

版本2:

static void gemm_v2(float* matA, float* matB, float* matC, const int M, const int N, const int K, const int strideA, const int strideB, const int strideC)
{
	for (int i = 0; i < M; i++)// 外層迴圈 為 行主導,仿存效果好
	{
		for (int j = 0; j < N; j++)
		{
			float sum = 0.0f;
			for (int k = 0; k < K; k++)
			{
				sum += matA[i*strideA + k] * matB[k*strideB + j];// 只有矩陣B是列訪問順序
			}
			matC[i*strideC + j] = sum;
		}
	}
}
gemm_v1比gemm_v2速度會慢很多,尤其是資料量比較大的時候。
因為在gemm_v1中,matB和matC的訪存以列為方向,會出現很多cache不命中的情況。
而在gemm_v2中則只有matB發生較多cache不命中,而這是gemm計算無法避免的。

在ncnn中,沒有將輸入和輸出展開成兩個大矩陣,會比較耗費記憶體;
就是按照卷積的方式,每次只讀取,運算的部分內容。
以卷積計算conv3x3_s1為例,
每次從matA同時訪問4行(一般一次3x3卷積只需要訪問3行),
由於step是1,所以可以同時生成2行的convolution結果。
可以看到有2行資料直接共用了,快取利用率得到極大提高。

快速矩陣乘法 參考

6. 單指令多資料指令SIMD 優化

SIMD即單指令多資料指令,
目前在x86平臺下有MMX/SSE/AVX系列指令,
arm平臺下有NEON指令。
一般SIMD指令通過intrinsics或者彙編實現。

SSE參考

SSE指令參考

neno參考

x86下 SSE程式設計

使用SSE指令,首先要了解這一類用於進行初始化載入資料以及將暫存器的資料儲存到記憶體相關的指令,
我們知道,大多數SSE指令是使用的xmm0到xmm8的暫存器,
那麼使用之前,就需要將資料從記憶體載入到這些暫存器。

資料的載入與儲存

 1. load系列,用於載入資料,從記憶體到暫存器。 ====傳入指標====
__m128 _mm_load_ss (float *p)   128位元組暫存器, 4*32=128
     用於scalar的載入,所以,載入一個單精度浮點數到暫存器的低位元組,
     其它三個單浮點單元清0,(r0 := *p, r1 := r2 := r3 := 0.0)。
     
__m128 _mm_load_ps (float *p)
     用於packed的載入(下面的都是用於packed的),要求p的地址是16位元組對齊,否則讀取的結果會出錯,
     (r0 := p[0], r1 := p[1], r2 := p[2], r3 := p[3])。
     
__m128 _mm_load1_ps (float *p)
__m128 _mm_loadh_pi (__m128 a, __m64 *p)
__m128 _mm_loadl_pi (__m128 a, __m64 *p)
__m128 _mm_loadr_ps (float *p)
__m128 _mm_loadu_ps (float *p)

 2. set系列,用於載入資料,大部分需要多條指令完成,但是可能不需要16位元組對齊。 =====傳入值====
__m128 _mm_set_ss (float w)   
     對應於_mm_load_ss的功能,不需要位元組對齊,需要多條指令。(r0 = w, r1 = r2 = r3 = 0.0)
__m128 _mm_set_ps (float z, float y, float x, float w)
     對應於_mm_load_ps的功能,引數是四個單獨的單精度浮點數,所以也不需要位元組對齊,需要多條指令。
     (r0=w, r1 = x, r2 = y, r3 = z,注意順序) 
__m128 _mm_set1_ps (float w)
     對應於_mm_load1_ps的功能,不需要位元組對齊,需要多條指令。(r0 = r1 = r2 = r3 = w)
__m128 _mm_setr_ps (float z, float y, float x, float w)
__m128 _mm_setzero_ps ()
         是清0操作,只需要一條指令。(r0 = r1 = r2 = r3 = 0.0)
     
 3.  store系列,用於將計算結果等SSE暫存器的資料儲存到記憶體中。
void _mm_store_ss (float *p, __m128 a)     _mm_store_ss:一條指令,*p = a0
void _mm_store_ps (float *p, __m128 a)     一條指令,p[i] = a[i]
void _mm_store1_ps (float *p, __m128 a)
void _mm_storeh_pi (__m64 *p, __m128 a)
void _mm_storel_pi (__m64 *p, __m128 a)
void _mm_storer_ps (float *p, __m128 a)
void _mm_storeu_ps (float *p, __m128 a)   一條指令,p[i] = a[i],不要求16位元組對齊。
void _mm_stream_ps (float *p, __m128 a)   直接寫入記憶體,不改變cache的資料。
這一系列函式和load系列函式的功能對應,基本上都是一個反向的過程。

計算指令

SSE提供了大量的浮點運算指令,包括加法、減法、乘法、除法、開方、最大值、最小值、近似求倒數、求開方的倒數等等,
可見SSE指令的強大之處。那麼在瞭解了上面的資料載入和資料儲存的指令之後,使用這些算術指令就很容易了,下面以加法為例。

SSE中浮點加法的指令有:
	__m128 _mm_add_ss (__m128 a, __m128 b); // a =(a0,a1,a2,a3)  b = (b0,b1,b2,b3) 結果=(a0+b0,a1,a2,a3)
	__m128 _mm_add_ps (__m128 a, __m128 b); // 結果=(a0+b0, a1+b1, a2+b2, a3+b3)
	其中,_mm_add_ss表示scalar執行模式,_mm_add_ps表示packed執行模式。

減法指令:
 	__m128 _mm_sub_ss (__m128 a, __m128 b); // 結果=(a0-b0,a1,a2,a3)
        __m128 _mm_sub_ps (__m128 a, __m128 b); // 結果=(a0-b0, a1-b1, a2-b2, a3-b3)
    乘法指令:
        __m128 _mm_mul_ss (__m128 a, __m128 b); 
	__m128 _mm_mul_ps (__m128 a, __m128 b);// 結果=(a0*b0, a1*b1, a2*b2, a3*b3)
    除法指令:
        __m128 _mm_div_ss (__m128 a, __m128 b); 
	__m128 _mm_div_ps (__m128 a, __m128 b);// 結果=(a0/b0, a1/b1, a2/b2, a3/b3)    
 加減混合運算:
        __m128 _mm_addsub_ps (__m128 a, __m128 b);  // 結果=(a0-b0, a1+b1, a2-b3, a3+b3)
 開平方 sqrt  去倒數 rcp 平方根的倒數 rsqrt 最小值min 最大值max
 
	
般而言,使用SSE指令寫程式碼,
步驟為:1. 使用load/set函式將資料從記憶體載入到SSE暫存器;
       2. 使用相關SSE指令完成計算等;
       3. 使用store系列函式將結果從暫存器儲存到記憶體,供後面使用。
// 示例程式
	float op1[4] = {1.0, 2.0, 3.0, 4.0}; // 浮點數陣列
	float op2[4] = {1.0, 2.0, 3.0, 4.0};
	float result[4];// 結果陣列

	__m128  a;
	__m128  b;
	__m128  c;

	// 1. Load  將資料從記憶體載入到 暫存器
	a = _mm_loadu_ps(op1);
	b = _mm_loadu_ps(op2);

	// 2. Calculate 進行邏輯計算
	c = _mm_add_ps(a, b);	// c = a + b

	// 3. Store 將資料從暫存器 儲存到 記憶體
	_mm_storeu_ps(result, c);

	/*		// Using the __m128 union to get the result.
	printf("0: %lf\n", c.m128_f32[0]);
	printf("1: %lf\n", c.m128_f32[1]);
	printf("2: %lf\n", c.m128_f32[2]);
	printf("3: %lf\n", c.m128_f32[3]);
	*/
	printf("0: %lf\n", result[0]);
	printf("1: %lf\n", result[1]);
	printf("2: %lf\n", result[2]);
	printf("3: %lf\n", result[3]);

ARM 下 NENO程式設計

ARM CPU最開始只有普通的暫存器,可以進行基本資料型別的基本運算。
自ARMv5開始引入了VFP(Vector Floating Point)指令,該指令用於向量化加速浮點運算。
自ARMv7開始正式引入NEON指令,NEON效能遠超VFP,因此VFP指令被廢棄。

ARMV7架構包含:

16個通用暫存器(32bit), R0-R15, R13為棧頂指標 Stack pointer, R14為Link registr, R15為程式計數器 Program Counter
16個NEON暫存器(128bit),Q0-Q15(同時也可以被視為32個64bit的暫存器,D0-D31)
16個VFP暫存器(32bit),  S0-S15
NEON和VFP的區別在於VFP是加速浮點計算的硬體不具備資料並行能力,同時VFP更盡興雙精度浮點數(double)的計算,NEON只有單精度浮點計算能力。

NEON指令和資料型別介紹

neon 和 sse綜合示例程式

專案工程


// 1. 在容器中填充隨機數===========

// 生成器generator:能夠產生離散的等可能分佈數值
// 分佈器distributions: 能夠把generator產生的均勻分佈值對映到其他常見分佈,
//                     如均勻分佈uniform,正態分佈normal,二項分佈binomial,泊松分佈poisson
static void fill_random_value(std::vector<float>& vec_data)
{        
        // 浮點數均勻分佈 分佈器    uniform_int_distribution(整數均勻分佈)
	std::uniform_real_distribution<float> distribution(
		std::numeric_limits<float>::min(),
		std::numeric_limits<float>::max());
        // 隨機數 生成器
	std::default_random_engine generator;
        // std::default_random_engine generator(time(NULL));// 配合隨機數種子
	
        // 為 vec_data 生成隨機數,傳入首尾迭代器和一個 lambda匿名函式
	// [變數擷取](引數表){函式提體}; [&](){}, 中括號內的& 表示函式可使用函式外部的變數。
	std::generate(vec_data.begin(), vec_data.end(), [&]() { return distribution(generator); });
}
// 下面是各種變數擷取的選項:
//   [] 不擷取任何變數
//   [&} 擷取外部作用域中所有變數,並作為引用在函式體中使用
//   [=] 擷取外部作用域中所有變數,並拷貝一份在函式體中使用
//   [=, &foo]   擷取外部作用域中所有變數,並拷貝一份在函式體中使用,但是對foo變數使用引用
//   [bar]       擷取bar變數並且拷貝一份在函式體重使用,同時不擷取其他變數
//   [this]      擷取當前類中的this指標。如果已經使用了&或者=就預設新增此選項。


// 2. 判斷兩vector是否相等====================================
static bool is_equals_vector(const std::vector<float>& vec_a, const std::vector<float>& vec_b)
{
	// 首先判斷 大小是否一致
	if (vec_a.size() != vec_b.size())
		return false;
        for (size_t i=0; i<vec_a.size(); i++)
	{
		if(vec_a[i] != vec_b[i]) // 浮點數可以這樣 判斷不相等??
			return false;
	}
	// 每個元素均相等
 	return true;
}

// 3. 正常的vector相乘(需要關閉編譯器的自動向量優化)====================
static void normal_vector_mul(const std::vector<float>& vec_a, 
                              const std::vector<float>& vec_b, 
			      std::vector<float>& vec_result)
{
	// 檢查 陣列維度
	assert(vec_a.size() == vec_b.size());
	assert(vec_a.size() == vec_result.size());
	
	// 迴圈遍歷相乘  編譯器可能會自動 進行向量化優化  新增標誌進行關閉 -ftree-vectorize
	for (size_t i=0; i<vec_result.size(); i++)
		vec_result[i] = vec_a[i] * vec_b[i];
}


// 4. neon優化的vector相乘======================================
static void neon_vector_mul(const std::vector<float>& vec_a, 
                              const std::vector<float>& vec_b, 
			      std::vector<float>& vec_result)
{
	// 檢查 陣列維度
	assert(vec_a.size() == vec_b.size());
	assert(vec_a
            
           

相關推薦

ncnn 框架分析 openmp加速 快取 仿 cache 快速矩陣乘法 指令資料指令SIMD

ncnn 框架分析 本文github連結 在ncnn中建立新層 ncnn 下載編譯使用 參考1 參考2 1. param 和 bin 檔案分析 param 7767517 # 檔案頭 魔數 75 83 # 層數量 輸入輸出blob數量

C++ openmp並行程序在linux上如何最大化使用cpu

.com 核心 程序 cpu pragma -m ron amp 指令 以上代碼中,#pragma omp parallel for 這一行的作用即是調用openmp的功能,根據檢測到的CPU核心數目,將for (i = 0; i < 1000000000; i++)

【Android開源專案分析】android輕量級開源快取框架——ASimpleCache(ACache)原始碼分析

ASimpleCache框架原始碼連結 官方介紹 ASimpleCache 是一個為android制定的 輕量級的 開源快取框架。輕量到只有一個java檔案(由十幾個類精簡而來)。 1、它可以快取什麼東西? 普通的字串、J

如何利用CPU來加速你的Linux命令 — awk, sed, bzip2, grep, wc等

你是否曾經有過要計算一個非常大的資料(幾百GB)的需求?或在裡面搜尋,或其它操作——一些無法並行的操作。資料專家們,我是在對你們說。你可能有一個4核或更多核的CPU,但我們合適的工具,例如 grep, bzip2, wc, awk, sed等等,都是單執行緒的,只能使

執行緒——pthread_setaffinity_np,cpulimit分析CPU資源對應用程式的影響

#define _GNU_SOURCE #include <stdio.h> #include <math.h> #include <pthread.h> cpu_set_t cpuset,cpuget; double waste_time(long n) { double

五種主要並行程式設計方法分析與比較

隨著多核時代的到來與流行,傳統的單執行緒序列程式的程式設計模式必將改變,取而代之的將是並行程式設計。目前已經有五種主要並行程式設計模型,下面將對此五種模型進行概括性的分析與比較: 1. MPI   MPI(Message Passing Interface)訊息傳遞介面是MPI論壇釋出的一個庫,而不是一門實

ARM處理器啟動過程分析

說明: 該流程圖按照程式碼執行時間順序劃分為4部分: 1.     Bootloader在圖片上半部,最先啟動; 2.     Kernel在圖片下半部,由bootloader引導啟動; 3.CPU0執行流程在圖片左半部,bootloader程式碼會進行判斷,先行啟

執行緒與處理器 SMP 分析

首先分析執行緒的優勢: 1、提高程式的併發性(執行緒級並行,而非指令級並行); 2、一個程序內的所有執行緒共享所有資源; 3、切換執行緒的代價下; 4、互動式程式可以通過多執行緒方式改善響應時間。 多執行緒實現的方式: 1、程式採用多執行緒設計方案; 2、多核處理器為單一的

YII框架分析筆記2:組件和事件行為管理

reac 設置 有變 相關 article class ces col cal Yii是一個基於組件、用於開發大型 Web 應用的高性能 PHP 框架。CComponent幾乎是所有類的基類,它控制著組件與事件的管理,其方法與屬性如下,私有變量$_e數據存放事件(evnet

AppStore App申請審加速

team blank style mil 審核 watermark eve .html span 有沒有遇到上線後發現非常嚴重的bug這種情況,修復bug後提交審核又是漫長的等待。那樣會把人逼瘋的。預計是為了相應這種情況,apple提供有一個加速審核的通道:https:/

編程雜談

變量 peter 重要 分布 process see 讀寫性能 visible 亂序 多核計算雜談--討論在多核編程時。在CPU和內存層次上應該知道的一些東西。嘗試找到協調多核工作的本質上的問題。 這裏討論基本上參考x86體系,然後依據須要簡化或改動。

Tensorflow實現Mask R-CNN實例分割通用框架,檢測,分割和特征點定位一次搞定(圖)

優點 設計 orf 時間 rcnn 超越 rain 沒有 add Mask R-CNN實例分割通用框架,檢測,分割和特征點定位一次搞定(多圖) 導語:Mask R-CNN是Faster R-CNN的擴展形式,能夠有效地檢測圖像中的目標,同時還能為每個實例生成一個

基於TI Davinci架構的/雙開發高速掃盲(以OMAP L138為例),dm8168開發參考以及達芬奇系列資料user guide整理

uwa 全部 dap setting pos eclips develop serial ger 基於TI Davinci架構的雙核嵌入式應用處理器OMAPL138開發入門 原文轉自http://blog.csdn.net/wangpengqi/article/de

odoo 8.0 啟用

itl 虛擬 jpg com 分享 roo aaa 成了 建議 對於很多企業來說,隨著時間的推移,用戶量或者企業建點擴張,使用erp就會出現應用訪問越來越慢的情況, 其實這種情況不但限於erp,只要是有數據量增長的互聯網業務必然會遇到的,因為一開始的是就

Zabbix通過SNMP監控CPU使用率時, 計算CPU平均使用率

mes ces zabb ext 觸發器 trend times http sso 環境:沒有Agent,只能通過SNMP監控時,需要獲取多核CPU的平均使用率。 ZABBIX的使用SNMP監控CPU使用率時,由於設備都是多核CPU,監控的都是單獨某一核心的使用率,但單獨某

linux top命令查看內CPU的使用講述【轉】

http 最小 文件 改變 總計 href 獲取 gif www. 轉載一下top使用後詳細的參數,之前做的筆記找不見了,轉載一下,作為以後的使用參考: 原文地址:http://blog.csdn.net/linghao00/article/details/8059244

微享商盟小程序開發系統框架分析

找不到 dos 再次 吸引 保持 廣告 掃碼 連接線 商業 從微信小程序發布這段時間,陸陸續續開發了不少小程序相關的項目,總結了一些通用性的組件,但是對於小程序如何做測試,依然是一頭霧水,直到做了不少的項目,積累的一些經驗和開源庫之後才理清如何做測試,下面將會介紹如何對

早起打卡贏現金小程序開發框架分析

文章 con 參數 查看 收集 alias 方法 由於 學生   早起打卡贏現金小程序開發 早起打卡贏現金小程序系統開發 【柯經理:184*7578*0020】 哇!!!媽媽再也不用擔心我賴床了。領導再也不用擔心我上班遲到了。早起打卡健康生活,營造良好生活習性。  早起

湯森路透 Thomson Reuters --使用模型數據庫ArangoDB 打造快速安全的簡潔視圖分析

廣泛 why 組合 方法 http href api 開發人員 圖片 摘要: 湯森路透為專業人士提供所需的智能,技術和人力資源,以便找到值得信賴的答案。它們使財務風險,法律,稅務會計以及媒體市場的專業人員能夠做出最重要的決定,所有這些都由世界上最值得信賴的新聞機構提供支持。

[py]你真的了解處理器嗎? 了解線程

註意 董事會 fusion 整體 用戶 繼續 高速 多核處理器 真的 你真的了解多核處理器嗎? 1.雙核≠雙性能 多核不一定會使你的手機或電腦速度更快,但它將提高你的PC的整體性能,這是一個有所不同的細微的技術特色。多核處理器的性能提升並不是簡單CPU核心的倍數,因為受到