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 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/151990.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

The ultimate UI kit and design system for Figma 组件库下载

Untitled UI 是世界上最大的 Figma UI 套件和设计系统。可以启动任何项目&#xff0c;为您节省数千小时&#xff0c;并祝您升级为专业设计师。 采用 100% 自动布局 5.0、变量、智能变体和 WCAG 可访问性精心制作。 900全局样式、变量&#xff1a;超级智能的全局颜色、排版和效…

代码随想录二刷 |数组 | 螺旋矩阵II

代码随想录二刷 &#xff5c; 数组 &#xff5c; 螺旋矩阵II 题目描述解题思路 & 代码实现 题目描述 29.螺旋矩阵II 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑电化学模型的配电网侧光储系统的分布式优化调度》

这个标题涉及到一个相当复杂和多层次的概念。让我们一步步解读这个标题&#xff1a; 配电网侧光储系统&#xff1a; "配电网" 是指用于将电力从输电网传输到最终用户的电力配送系统。"光储系统" 可能是指光伏发电系统&#xff08;太阳能发电&#xff09;和…

C++中结构体的初始化

C中结构体的初始化 结构体是一个由程序员定义的数据类型&#xff0c;可以容纳许多不同的数据值。在过去&#xff0c;面向对象编程的应用尚未普及之前&#xff0c;程序员通常使用这些从逻辑上连接在一起的数据组合到一个单元中。一旦结构体类型被声明并且其数据成员被标识&…

如何用Java设计自动售货机?

如何用Java设计自动售货机?是大多在高级Java开发人员面试中经常被问到的好问题之一。在典型的编码面试中,你会得到一个问题描述来开发一个售货机,在有限的时间内,通常2到3小时内,你需要在Java中编写设计文档、工作代码和单元测试。这种Java面试的一个关键优势是可以一次测试候…

【机器学习】特征工程:特征预处理,归一化、标准化、处理缺失值

特征预处理采用的是特定的统计方法&#xff08;数学方法&#xff09;将数据转化为算法要求的数字 1. 数值型数据 归一化&#xff0c;将原始数据变换到[0,1]之间 标准化&#xff0c;数据转化到均值为0&#xff0c;方差为1的范围内 缺失值&#xff0c;缺失值处理成均值、中…

cvf_使用lora方法增强能力

cvf_使用lora方法增强能力 实验对比图最终代码简介详细解析实验对比图 最终代码 import paddle import numpy as np import pandas as pd from tqdm import tqdmclass FeedFroward(paddle.nn.Layer)

5.什么是Spring的依赖注入(DI)?IOC和DI的区别是什么

很多人把IOC和DI说成一个东西&#xff0c;笼统来说的话是没有问题的&#xff0c;但是本质上还是有所区别的,希望大家能够严谨一点&#xff0c; IOC和DI是从不同的角度描述的同一件事&#xff0c;IOC是从容器的角度描述&#xff0c;而DI是从应用程序的角度来描述&#xff0c;也…

LeetCode977.有序数组的平方(双指针法、暴力法、列表推导式)

LeetCode977.有序数组的平方 1.问题描述2.解题思路3.代码4.知识点 1.问题描述 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] …

如何使用Python开发Qt应用的自动化测试!

随着软件开发领域持续进步&#xff0c;Qt桌面应用的测试方法也在逐渐演变。传统上&#xff0c;Qt应用的测试主要依赖于测试工程师手工执行用例&#xff0c;这种方法虽然在某些方面有效&#xff0c;但对于大型应用而言&#xff0c;手工测试的耗时过长&#xff0c;且在可靠性和覆…

Golang: Store Query Result in a Map

目录 1. Golang: Store Query Result in a Map1.1. Using Structs1.2. Using Maps 1. Golang: Store Query Result in a Map 注意: 使用这个可能会造成列名和列值乱串的现象&#xff0c;解决这个可以使用 AS 语法&#xff1a; SELECT TENANT_ID AS TENANT_ID,SVR_IP AS SVR_IP,…

程序员导航站

探路者 hello.alluniverse.vip 开发者导航 - Pro Developer网站导航 探路者是一款极简导航工具&#xff0c;致力于收录的每个站点都有其独特的作用。同时支持自定义导航&#xff0c;让用户快速实现个性化的导航站点。 特性概述 免费ChatGPT 装机必备 开发工具 Git精选项目 …

LabVIEW编程开发NI-USRP

LabVIEW编程开发NI-USRP 可编程性是SDR的关键特性&#xff0c;它使人们能够将无线电外围设备转换为先进的无线系统。USRP是市场上最开放、最通用的SDR&#xff0c;可帮助工程师在主机和FPGA上使用各种软件开发工具构建系统。 有多种选项可用于对基于SDR的系统的主机进行编程。…

9 HDFS架构剖析

问题 100台服务器&#xff0c;存储空间单个200GB 20T 5T文件如何存储&#xff1f; 128MB一块 128MB81GB 1288*10241TB 5T数据分成的128MB的块数 8192 * 5 客户端(client)代表用户通过与namenode和datanode交互来访问整个文件系统。 HDFS集群有两类节点&#xff1a; 一个na…

Python武器库开发-flask篇之error404(二十七)

flask篇之error404(二十七) 首先&#xff0c;我们先进入模板的界面创建一个404的html页面 cd templates vim 404.html404.html的内容如下&#xff1a; <h1>error!!!</h1>在 Flask 应用程序中&#xff0c;当用户访问一个不存在的页面的时候&#xff0c;会出现 4…

MAC上修改mysql的密码(每一步都图文解释哦)

当你想要连接本机数据库时&#xff0c;是不是有可能突然忘记了自己的数据库密码? 在此文中&#xff0c;我们来详细解决一下如何去修改自己的数据库密码&#xff0c;并使用Navicat来连接测试 1.停止mysql服务 打开终端&#xff0c;键入命令,将mysql服务先停止掉&#xff0c;…

JC/T 2339-2015 地暖用相变储能材料及构件检测

相变储能材料是指利用相变过程吸收/释放热量并能与地暖配套使用的材料及构件。 JC/T 2339-2015 地暖用相变储能材料及构件测试项目 测试项目 测试标准 相变温度 JC/T 2111 相变潜热 JC/T 2111 材料寿命 JC/T 2339 单位面积相变储能量 JC/T 2339 耐冷循环性能 JC/T …

归并排序详解:递归实现+非递归实现(图文详解+代码)

文章目录 归并排序1.递归实现2.非递归实现3.海量数据的排序问题 归并排序 时间复杂度&#xff1a;O ( N * logzN ) 每一层都是N,有log2N层空间复杂度&#xff1a;O&#xff08;N&#xff09;&#xff0c;每个区间都会申请内存&#xff0c;最后申请的数组大小和array大小相同稳定…

智能指针面试题

智能指针被问到的概率还是很大的&#xff0c;特别是Shared_ptr&#xff0c;最好会手撕&#xff0c;亲身经历&#xff01; 基本概念 1. RAll RAII&#xff08;Resource Acquisition Is Initialization&#xff09;是一种利用对象生命周期来控制程序资源&#xff08;如内存、文…

(Transfer Learning)迁移学习在IMDB上训练情感分析模型

1. 背景 有些场景下&#xff0c;开始的时候数据量很小&#xff0c;如果我们用一个几千条数据训练一个全新的深度机器学习的文本分类模型&#xff0c;效果不会很好。这个时候你有两种选择&#xff0c;1.用传统的机器学习训练&#xff0c;2.利用迁移学习在一个预训练的模型上训练…