SIMD指令集介绍

2023-11-21 02:52
文章标签 介绍 指令集 simd

本文主要是介绍SIMD指令集介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

# 介绍

本学期,我们将在多项作业中使用 SIMD(单指令多数据)指令。这些是在称为向量的宽寄存器上运行的指令集。对于我们的作业,这些向量通常为 256 位宽,尽管您可能偶尔使用 128 位版本。通常,作用于这些宽寄存器的指令会将其视为值数组。然后,他们将对数组中的每个值独立执行操作。在硬件中,这可以通过多个并行工作的ALU来实现。因此,尽管这些指令执行的算术比“正常”指令多很多倍,但它们可以与正常指令一样快。

通常,我们将使用“内部函数”访问这些指令。这些函数通常直接对应于特定的汇编指令。这将使我们能够编写始终如一地访问此特殊功能的 C 代码,而不会失去拥有 C 编译器的所有好处。

#  内联参考

我们将使用的内在功能是英特尔定义的接口。因此,英特尔的文档(可在此处找到)是这些功能的综合参考。请注意,本文档包含与实验室计算机上不支持的指令相对应的函数。为避免看到这些,请务必仅选中侧面标有“AVX”、“AVX2”和“SSE”到“SSE4.2”的框。

英特尔的参考资料通常描述了伪代码中的指令,这些指令使用诸如

```
a[63:0] := b[127:64]
```

表示将向量 B 的位 64 到 127(含)分配给向量 A 的位 0 到 63。

#  头文件

若要使用内部函数,需要包含相应的头文件。对于内联函数,我们将使用它是:

```
#include <smmintrin.h>
#include <immintrin.h>
```

# 用 C 语言表示向量

为了表示可能存储在 C 寄存器之一中的 256 位值,我们将使用以下类型之一:

* __m256 (8个float)
* __m256d (4个double)
* __m256i (n个int)

由于其中每个都只是一个 256 位值,因此,如果要使用的函数需要“错误”类型的值,则可以在这些类型之间进行转换。例如,您可能希望使用旨在加载浮点值以加载整数的函数。在内部,期望这些类型的函数只是操作寄存器或内存中的 256 位值。

## 类型和内部函数的 128 位版本

还有 128 位矢量类型和相应的指令。要使用它,在大多数情况下,您可以在类型名称中替换为 _mm256_ 和 _mm_ __m128 在类型名称中替换为 __m256 。

在某些情况下,仅存在 256 位版本的指令。

## 设置和提取值

如果要加载 128 位值的常量,则需要使用内部函数之一。最容易的是,您可以使用名称以 开头 _mm_setr 的函数之一。例如:

```
__m256i values = _mm256_setr_epi32(0x1234, 0x2345, 0x3456, 0x4567, 0x5678, 0x6789, 0x789A, 0x89AB);
```

make 包含 values 8 个 32 位整数, , , 0x3456 , 0x4567 0x1234 0x2345 0x5678 , , , , 0x6789 0x789A . 0x89AB 然后,我们可以通过执行以下操作来提取这些整数中的每一个:

```
int first_value = _mm256_extract_epi32(values, 0);
// first_value == 0x1234
int second_value = _mm256_extract_epi32(values, 1);
// second_value == 0x2345
```

请注意,只能将常量索引传递给 和类似函数的 _mm256_extract_epi32 第二个参数。

## 加载和存储值

要从内存加载值数组或将值数组存储到内存中,我们可以使用以 或 _mm256_storeu 开头 _mm256_loadu 的内联函数:

```
int arrayA[8];
_mm256_storeu_si256((__m128i*) arrayA, values);
// arrayA[0] == 0x1234
// arrayA[1] == 0x2345
// ...

int arrayB[8] = {10, 20, 30, 40, 50, 60, 70, 80};
values = _mm256_loadu_si256((__m128i*) arrayB);
// 10 == arrayB[0] == _mm256_extract_epi32(values, 0)
// 20 == arrayB[1] == _mm256_extract_epi32(values, 1)
// ...
```

##  算术

要实际对值执行算术运算,每个支持的数学运算都有函数。例如:

```
__m256i first_values =  _mm256_setr_epi32(10, 20, 30, 40);
__m256i second_values = _mm256_setr_epi32( 5,  6,  7,  8);
__m256i result_values = _mm256_add_epi32(first_values, second_values);
// _mm_extract_epi32(result_values, 0) == 15
// _mm_extract_epi32(result_values, 1) == 26
// _mm_extract_epi32(result_values, 2) == 37
// _mm_extract_epi32(result_values, 3) == 48
```

## 向量中不同类型的值

这些示例将 256 位值视为 8 个 32 位整数的数组。有一些指令处理许多不同类型的值,包括其他大小的整数或浮点数。您通常可以通过函数名称中指示值类型的存在来判断需要哪种类型。例如,“epi32”表示 an __m256 中的“8 个 32 位值”或 ( __m128 名称代表“扩展打包整数,32 位”)。您将在名称中看到其他一些约定:

* si256 – 有符号 256 位整数
* si128 – 有符号 128 位整数
* epi8 , , epi32 — epi64 有符号 8 位整数(A 中的 32 个和 A __m256 __m128 中的 16 个)或有符号 32 位整数或有符号 64 位整数的向量
* epu8 — 无符号 8 位整数的 vecotr(当操作对有符号和无符号数字的操作之间存在差异时,例如转换为更大的整数或乘法)
* epu16 , epu32 — 无符号 16 位整数或 8 个无符号 32 位整数数组(当操作与有符号不同时)
* ps — “打包单” — 8 个单精度浮子
* pd — “打包双倍” — 4 双倍
* ss — 一个浮点数(仅使用 256 位或 128 位值的 32 位)
* sd — 一个双精度值(仅使用 256 位或 256 位值的 64 位)

#  示例(在 C 中)

以下两个 C 函数是等效的

```
int add_no_AVX(int size, int *first_array, int *second_array) {
    for (int i = 0; i < size; ++i) {
        first_array[i] += second_array[i];
    }
}

int add_AVX(int size, int *first_array, int *second_array) {
    int i = 0;
    for (; i + 8 <= size; i += 8) {
        // load 256-bit chunks of each array
        __m256i first_values = _mm_loadu_si256((__m256i*) &first_array[i]);
        __m256i second_values = _mm_loadu_si256((__m256i*) &second_array[i]);

        // add each pair of 32-bit integers in the 256-bit chunks
        first_values = _mm256_add_epi32(first_values, second_values);
    
        // store 256-bit chunk to first array
        _mm_storeu_si256((__m256i*) &first_array[i], first_values);
    }
    // handle left-over
    for (; i < size; ++i) {
        first_array[i] += second_array[i];
    }
}
```

# 精选的方便的内在函数:

##  算术

* _mm256_add_epi32(a, b) — 将其 __m256i 参数视为 8 个 32 位整数。如果 a 包含 32 位整数 a0, a1, a2, a3, a4, a5, a6, a7 并 b 包含 b0, b1, b2, b3, b4, b5, b6, b7 ,则返回 a0 + b0, a1 + b1, a2 + b2, a3 + b3, a4 + b4, a5 + b5, a6 + a6, a7 + b7 。(与 vpaddd 指令相对应。
* _mm256_add_epi16(a, b) — 与 _mm256_add_epi32 16 位整数相同,但使用 16 位整数。如果 a 包含 16 位整数 a0, a1, ..., a15 并 b 包含 b1, b2, ..., b15 ,则返回 a0 + b0, a1 + b1, ..., a15 + b15 。(与 vpaddw 指令相对应。
* _mm256_add_epi8(a, b) — 与 _mm256_add_epi32 8 位整数相同,但使用 8 位整数。
* _mm256_mullo_epi16(x, y) :将 x 和 y 视为 16 位有符号整数的向量,将每对整数相乘,并将结果截断为 16 位。
* _mm256_mulhi_epi16(x, y) :将 x 和 y 视为 16 位有符号整数的向量,将每对整数相乘得到一个 32 位整数,然后返回每个 32 位整数结果的前 16 位。
* _mm256_srli_epi16(x, N) :处理 x 和 16 位有符号整数的向量,并返回逻辑上将每个右移的结果 N 。(还有 epi32 32 位或 64 位整数的 and epi64 变体。
* _mm256_slli_epi16(x, N) :处理 x 和 16 位有符号整数的向量,并返回将每个向左移动的结果 N 。(还有 epi32 32 位或 64 位整数的 and epi64 变体。
* _mm256_hadd_epi16(a, b) — (“horizontal add”) 将其 __m128i 参数视为 16 位整数的向量。如果 a contains 和 b contains b0, b1, b2, b3, ..., b15 a0, a1, a2, a3, ..., a15 ,则返回 a0 + a1, a2 + a3, a4 + a5, a6 + a7, b0 + b1, b2 + b3, b4 + b5, b6 + b7, a8 + a9, a10 + a11, a12 + a13, a14 + a15, b8 + b9, b10 + b11, b12 + b13, b14 + b15 。请注意,这通常比 _mm_add_epi16 慢得多。(与 vphaddw 指令相对应。

##  加载/存储

* _mm256_loadu_si256 , _mm256_storeu_si256 — 向内存加载或存储 256 位或从内存加载或存储 256 位。请注意,您可以使用 _mm256_storeu_si256 存储到临时数组中,如下所示:

  ```
   unsigned short values_as_array[16];
   __m256i values_as_vector;

   _mm256_storeu_si128((__m256i*) &values_as_array[0], values_as_vector);
  ```
* _mm_loadu_si128 , _mm_storeu_si128 — 将 128 位加载或存储到内存或从内存加载或存储。(对应于 vmovdqu 说明。它们的工作方式与 完全相同, _mm256_loadu_si256 只是它们使用 type __m128i 而不是 __m256i .
* 要存储向量中的 64 位或 32 位,一种方法是使用提取操作和 memcpy:

  ```
   unsigned short first_four_values_as_array[4];
   __m256i values_as_vector;

   *(long*)(&first_four_values_as_array[0]) = _mm256_extract_epi64(values_as_vetor, 0);
  ```

  (此代码实际上不是标准投诉;它违反了“严格别名”规则。但是在 SIMD 分配的 Makefile 中,我们使用 compiler 选项 -fno-strict-aliasing 禁用了它。不违反严格别名规则的替代方法是使用联合,而不是将指针转换为 int* or to use memcpy ,这通常针对小副本进行了优化。
* _mm_cvtsi32_si128 :将 32 位加载到 128 位向量中:

  ```
   unsigned short values[2];
   __m128i values_as_vector; // only using first 32 bits = 2 shorts
   values_as_vector = _mm_cvtsi32_si128( *(int*) &values[0]);
  ```

  (此代码实际上不是标准投诉;它违反了“严格别名”规则。但是在 SIMD 分配的 Makefile 中,我们使用 compiler 选项 -fno-strict-aliasing 禁用了它。不违反严格别名规则的替代方法是使用联合,而不是将指针转换为 int* .)
* _mm_cvtsi32_si128 :将 64 位加载到 128 位向量中:

  ```
   unsigned short values[4];
   __m128i values_as_vector; // only using first 64 bits = 4 shorts
   values_as_vector = _mm_cvtsi64_si128( *(long*) &values[0]);
  ```

  (此代码实际上不是标准投诉;请参阅上面的评论) _mm_cvtsi32_si128
* 要在 256 位向量中加载 32 位或 64 位,可以使用 _mm_cvtsi32_si128 或 _mm_cvtsi32_si256 一起使用 _mm266_zextsi128_si256 将 128 位向量转换为 256 位向量。
* _mm256_maskstore_epi32(int *addr, __m256i mask, __m256i a) — 存储 a at addr 的 32 位值,但仅存储 mask 指定的 32 位值。如果设置了每个 32 位整数 mask 的最高有效位(即符号),则存储值。例如:

  ```
   int values[8] = { 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF };
   __m256i a =    __m256_setr_epi32(1,2,3,4,5,6,7,8);
   __m256i mask = __m256_setr_epi32(0,-1,0,0,-1,0,-1,-1);
   _mm256_maskstore_epi32(&values[0], mask, a);
  ```

  应导致包含以下内容的值

  ```
   { 0xF, 2, 0xF, 0xF, 5, 0xF, 7, 8 }
  ```
* 有关详细信息,请参阅英特尔的参考资料,在“加载”和“存储”类别下

##  设置常量

* _mm256_setr_epi32 — 返回一个 __m256i 包含指定 32 位整数的值。第一个整数参数将位于写入内存时地址最低的部分 __m256i 。例如:

  ```
   __m256i value1 = _mm256_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7);
  ```

  产生 value1 与 in value2 相同的结果

  ```
   int array[8] = {0, 1, 2, 3, 4, 5, 6, 7};
   __m256i value2 = _mm256_loadu_si256((__m256i*) &array[0]);
  ```
* _mm_setr_epi32 — 返回一个 __m128i 包含指定 32 位整数的值。第一个整数参数将位于写入内存时地址最低的部分 __m128i 。例如:

  ```
   __m128i value1 = _mm_setr_epi32(0, 1, 2, 3);
  ```

  产生 value1 与 in value2 相同的结果

  ```
   int array[4] = {0, 1, 2, 3, 4, 5, 6, 7};
   __m128i value2 = _mm_loadu_si128((__m256i*) &array[0]);
  ```
* _mm256_setr_epi16 — 与 _mm256_setr_epi32 16 位整数相同,但使用 16 位整数。例如:

  ```
   __m256i value1 = _mm256_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
  ```

  产生 value1 与 in value2 相同的结果

  ```
   short array[8] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
   __m256i value2 = _mm256_loadu_si256((__m256i*) &array[0]);
  ```
* _mm256_setr_epi8 , — 与 _mm256_setr_epi32 和 _mm_setr_epi32 相同, _mm_setr_epi8 但具有 8 位整数。
* _mm_set1_epi32 , , — 返回一个 __m128i 值,表示适当大小的值数组, _mm_set1_epi16 _mm_set1_epi8 其中数组的每个元素都具有相同的值。例如:

  ```
   __m128i value = _mm_set1_epi16(42);
  ```

  具有与以下相同的效果:

  ```
   __m128i value = _mm_setr_epi16(42, 42, 42, 42,  42, 42, 42, 42);
  ```
* _mm256_set_epi8 , etc. — 与 _mm256_setr_epi8 等相同,但其参数的顺序相反
* 有关更多信息,请参阅英特尔的参考资料,在“设置”类别下

## 提取部分值

* _mm256_extract_epi32(a, index) 从 256 位向量中提取 index 'th 32 位整数 a 。索引为 0 的整数是将存储在最低内存地址的整数,如果 a 复制到内存中。 index 必须是一个常量。
   例如

  ```
   __m256i a = _mm256_setr_epi32(0, 10, 20, 30, 40, 50, 60, 70);
   int x = _mm256_extract_epi32(a, 2);
  ```

  20 分配给 x 。
* _mm_extract_epi32(a, index) 从 128 位向量中提取 index 'th 32 位整数 a 。 index 必须是常量。
* _mm256_extract_epi16(a, index) 与 _mm256_extract_epi32 16 位整数相同,但具有 16 位整数
* _mm256_extracti128_si256(a, index) 从 256 位向量中提取 index 128 位向量 a 。 index 必须是常量。
   例如

  ```
   __m256i a = _mm256_setr_epi32(0, 10, 20, 30, 40, 50, 60, 70);
   __m128i result = _mm256_extracti128_si256(a, 1);
  ```

   相当于

  ```
   __m128i result = _mm_setr_epi32(40, 50, 60, 70);
  ```
* 有关更多信息,请参阅英特尔的参考资料,搜索“提取”或在“Swizzle”和“Cast”类别下查找。

## 在值类型之间转换

* _mm256_cvtepu8_epi16(eight_bit_numbers) :采用包含 16 个 8 位数字的 128 位向量,并将其转换为包含 16 个 16 位有符号整数的 256 位向量。例如:

  ```
   __m128i value1 = _mm_setr_epi8(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150);
   __m256i value2 = _mm256_cvtepu8_epi16(value1);
  ```

  导致 value2 包含与我们执行的相同的值:

  ```
   __m256i value2 = _mm256_setr_epi16(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150);
  ```
* []()_mm256_packus_epi16(a, b) 获取 256 位向量中的 16 位有符号整数 a , b 并将它们转换为 8 位无符号整数的 256 位向量。结果包含 的前 a 8 个整数,后跟 的前 8 个整数,后跟 的最后 8 个整数 b a ,后跟 的最后 8 个整数 b 。超出范围的值设置为 255 或 0。
   例如:

  ```
   __m256i a = _mm256_setr_epi16(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160);
   __m256i b = _mm256_setr_epi16(170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 25, 15, 5, -5, -15);

   __m256i result = _mm256_packus_epi16(a, b)
  ```

  设置 result 与我们所做的相同:

  ```
   __m256i result = _mm256_setr_epu8(
       10, 20, 30, 40, 50, 60, 70, 80, /* first 8 integers from a */
       170, 180, 190, 200, 210, 220, 230, 240, /* first eight integers from b */
       90, 100, 110, 120, 130, 140, 150, /* last 8 integers from a */
       250, 255, 255, 25, 15, 5, 0, 0,  /* last 8 integers from b */
           /* 260, 270 became 255;  -5, -15 became 0 */
   );
  ```
* _mm256_zextsi128_si256(a) 采用 128 位向量 a ,并通过添加 0 将其转换为 256 位向量。
* 有关更多信息,请参阅英特尔在“Swizzle”和“Move”和“Cast”类别下的参考。

## 重新排列 256 位值

* _mm256_permute2x128_si256(a, b, mask) 采用两个 256 位向量, a 并 b 根据 mask 将这些向量的 128 位半部分组合成一个新的 256 位向量。 mask 是一个单字节整数常量。最低有效半字节指定放置在结果向量的最低地址中的值,最高有效半字节指定放置在结果向量的最高地址中的值。
  每个掩码半字节选择的值为:

  * 0 选择前 128 位 a
  * 1 选择第二个 128 位 a
  * 2 选择前 128 位 b
  * 3 选择第二个 128 位 b
  * 4 到 15 选择常量 0 (忽略 a 和 b 的值)

  例如,要重复 a 的第二个 128 位,可以提供如下示例所示 0x11 的掩码:

  ```
   __m256i a = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7);
   __m256i b = _mm256_setr_epi32(8, 9, 10, 11, 12, 13, 14, 15);
   __m256i result = _mm256_permute2x128_si256(a, b, 0x11);
   // result == _mm256_setr_epi32(4, 5, 6, 7, 4, 5, 6, 7)
  ```

  要生成前 128 位后跟后跟 1 位后跟 128 位 a b 的结果,将提供如下 0x30 掩码:

  ```
   __m256i a = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7);
   __m256i b = _mm256_setr_epi32(8, 9, 10, 11, 12, 13, 14, 15);
   __m256i result = _mm256_permute2x128_si256(a, b, 0x30);
   // result == _mm256_setr_epi32(0, 1, 2, 3, 12, 13, 14, 15)
  ```
* _mm256_unpackhi_epi16(a, b) 将 16 位整数与 256 位向量中每个 128 位半部分的上四分之一交错, a 然后 b .例如:

  ```
   __m256i a = _mm256_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
   __m256i b = _mm256_setr_epi16(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31);
   __m256i result = _mm256_unpackhi_epi16(a, b);
  ```

  与

  ```
   __m256i result = _mm256_setr_epi16(
       /* top quarter of first half of a and b */
       4, 20, 5, 21, 6, 22, 7, 23,
       /* top quarter of second half of a and b */
       12, 28, 13, 29, 14, 30, 15, 31
   )
  ```
* _mm256_unpacklo_epi16(a, b) 就像, _mm256_unpackhi_epi16 但它从 和 的每半部分 a b 的底部四分之一取 16 位整数
* _mm256_permutevar8x32_epi32(x, indexes) — 通过为向量中的每个 32 位索引生成一个 32 位值的向量 indexes ,从向量中检索该索引处的 32 位值 x 并将其放入结果中。例如:

  ```
   __m256i x = _mm256_setr_epi32(10, 20, 30, 40, 50, 60, 70, 80)
   __m256i indexes = _mm256_setr_epi32(3, 3, 0, 1, 2, 3, 6, 7);
   __m256i result = _mm256_permutevar8x32_epi32(x, indexes)
  ```

  等同于:

  ```
   __m256i reuslt = _mm256_setr_epi32(40, 40, 10, 20, 30, 70, 80);
  ```
* 有关更多信息,请参阅英特尔在“Swizzle”和“Move”以及“Cast”和“Shift”类别下的参考。

## 重新排列 128 位值

* _mm_unpackhi_epi16(a, b) 将 128 位向量上半部分的 16 位整数交错, a 然后 b .例如:

  ```
   __m128i a = _mm_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7);
   __m128i b = _mm_setr_epi16(8, 9, 10, 11, 12, 13, 14, 15);
   __m256i result = _mm_unpackhi_epi16(a, b);
  ```

  与

  ```
   __m128i result = _mm_setr_epi16(
       4, 20, 5, 21, 6, 22, 7, 23,
   )
  ```
* _mm_shuffle_epi8(a, mask) 重新排列 a 根据 的 mask 字节并返回结果。 mask 是 8 位整数 (type __m128i ) 的向量,指示如何重新排列每个字节:

  * 如果掩码中的字节设置了高位(大于 127),则输出的相应字节为 0;
  * 否则,输入中指定的字节号将复制到输出的相应字节。字节使用 0 进行编号,以表示如果将向量复制到内存中,将存储在最低地址中的字节。

   例如:

  ```
   __m128i value1 = _mm_setr_epi8(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160);
   __m128i mask = _mm_setr_epi8(0x80, 0x80, 0x80, 5, 4, 3, 0x80, 7, 6, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
   __m128i value2 = _mm_shuffle_epi8(value1, mask);
  ```

  应产生与以下结果相同的结果:

  ```
   __m128i value2 = _mm_setr_epi8(0, 0, 0, 60, 50, 40, 0, 80, 70, 0, 0, 0, 0, 0, 0, 0, 0);
       /* e.g. since 3rd element of mask is 5, 3rd element of output is 60, element 5 of the input */
  ```
* 有关更多信息,请参阅英特尔在“Swizzle”和“Move”以及“Cast”和“Shift”类别下的参考。

# 示例(组装指令)

 指令

```
      paddd %xmm0, %xmm1

```

接收两个 128 位值,一个在寄存器中,另一个在寄存器 %xmm0 %xmm1 中。这些寄存器中的每一个都被视为两个 64 位值的数组。将每对 64 位值相加,并将结果存储在 %xmm1 中。

例如,如果 %xmm0 包含 128 位值(以十六进制写入):

```
0x0000 0000 0000 0001 FFFF FFFF FFFF FFFF 
```

并 %xmm1 包含 128 位值(以十六进制写入):

```
0xFFFF FFFF FFFF FFFE 0000 0000 0000 0003 
```

然后 %xmm0 ,将被视为包含数字和 (或 0xFFFFFFFFFFFFFFFF ),并 %xmm1 被视为包含数字 -2 1 和 -1 3 。 paddd 将添加 1 和 -2 to produce -1 and -1 and 3 to produce 2, so the final value of %xmm1' 将是:

```
0xFFFF FFFF FFFF FFFF 0000 0000 0000 0002
```

如果我们将此值解释为两个 64 位整数的数组,则为 -1 和 2 。

这篇关于SIMD指令集介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/399323

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

Mysql BLOB类型介绍

BLOB类型的字段用于存储二进制数据 在MySQL中,BLOB类型,包括:TinyBlob、Blob、MediumBlob、LongBlob,这几个类型之间的唯一区别是在存储的大小不同。 TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

nginx介绍及常用功能

什么是nginx nginx跟Apache一样,是一个web服务器(网站服务器),通过HTTP协议提供各种网络服务。 Apache:重量级的,不支持高并发的服务器。在Apache上运行数以万计的并发访问,会导致服务器消耗大量内存。操作系统对其进行进程或线程间的切换也消耗了大量的CPU资源,导致HTTP请求的平均响应速度降低。这些都决定了Apache不可能成为高性能WEB服务器  nginx:

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器