18. WebGPU 计算着色器

news2024/11/18 14:43:27

本文接续基础知识文章。我们将从一些基本的计算着色器开始,然后希望继续讨论解决现实世界问题并写一些示例。

在上一篇文章中,制作了一个非常简单的计算着色器,可以将数字加倍。

这是着色器

@group(0) @binding(0) var<storage, read_write> data: array<f32>;
 
@compute @workgroup_size(1) fn computeSomething(
  @builtin(global_invocation_id) id: vec3<u32>
) {
  let i = id.x;
  data[i] = data[i] * 2.0;
}

然后像这样有效地运行计算着色器

 ...
  pass.dispatchWorkgroups(count);

我们需要回顾一下工作组的定义。

可以将工作组视为 小的线程集合。每个线程并行运行。在 WGSL 中静态定义工作组的大小。工作组大小以 3 个维度定义,缺省时每个维度默认为 1,因此 @workgroup_size(1) 等同于 @workgroup_size(1, 1, 1) 。

如果将工作组定义为 @workgroup_size(3, 4, 2) ,那么将定义 3 * 4 * 2 个线程,或者换句话说,每个工作组有 24 线程。

在这里插入图片描述

If we then call pass.dispatchWorkgroups(4, 3, 2) we’re saying, execute a workgroup of 24 threads, 4 * 3 * 2 times (24) for a total of 576 threads.

如果随后调用 pass.dispatchWorkgroups(4, 3, 2) ,意思是,执行一个包含 24 个线程的工作组,4 * 3 * 2 次 (24),总共 576 个线程 【(43 2)(3 4* 2)=24*24=576】。

在这里插入图片描述

在计算着色器的每个“调用(invocation)”中,以下内置(builtin )变量可用。

  • local_invocation_id: The id of this thread within a workgroup
    See the diagram above.
    见上图。
  • workgroup_id: The id of the workgroup.

工作组中的每个线程都将具有相同的工作组 ID。见上图。

  • global_invocation_id: A unique id for each thread

    可以认为是

      global_invocation_id = workgroup_id * workgroup_size +local_invocation_id 
    
  • num_workgroups: What you passed to pass.dispatchWorkgroups

  • local_invocation_index: The id of this thread linearized

    你可以认为这是

    rowSize   = workgroup_size.x 
    sliceSize = rowWidth* workgroup_size.y 
    local_invocation_index =
          local_invocation_id.x +
          local_invocation_id.y * rowSize +
          local_invocation_id.z * sliceSize 
    

让我们制作一个示例来使用这些值。只需将每次调用的值写入缓冲区,然后打印出这些值

这是着色器

const dispatchCount = [4, 3, 2];
const workgroupSize = [2, 3, 4];
 
// multiply all elements of an array
const arrayProd = arr => arr.reduce((a, b) => a * b);
 
const numThreadsPerWorkgroup = arrayProd(workgroupSize);
 
const code = `
// NOTE!: vec3u is padded to by 4 bytes
@group(0) @binding(0) var<storage, read_write> workgroupResult: array<vec3u>;
@group(0) @binding(1) var<storage, read_write> localResult: array<vec3u>;
@group(0) @binding(2) var<storage, read_write> globalResult: array<vec3u>;
 
@compute @workgroup_size(${workgroupSize}) fn computeSomething(
    @builtin(workgroup_id) workgroup_id : vec3<u32>,
    @builtin(local_invocation_id) local_invocation_id : vec3<u32>,
    @builtin(global_invocation_id) global_invocation_id : vec3<u32>,
    @builtin(local_invocation_index) local_invocation_index: u32,
    @builtin(num_workgroups) num_workgroups: vec3<u32>
) {
  // workgroup_index is similar to local_invocation_index except for
  // workgroups, not threads inside a workgroup.
  // It is not a builtin so we compute it ourselves.
 
  let workgroup_index =  
     workgroup_id.x +
     workgroup_id.y * num_workgroups.x +
     workgroup_id.z * num_workgroups.x * num_workgroups.y;
 
  // global_invocation_index is like local_invocation_index
  // except linear across all invocations across all dispatched
  // workgroups. It is not a builtin so we compute it ourselves.
 
  let global_invocation_index =
     workgroup_index * ${numThreadsPerWorkgroup} +
     local_invocation_index;
 
  // now we can write each of these builtins to our buffers.
  workgroupResult[global_invocation_index] = workgroup_id;
  localResult[global_invocation_index] = local_invocation_id;
  globalResult[global_invocation_index] = global_invocation_id;
`;

使用了 JavaScript 字符串模板,因此可以从 JavaScript 变量 workgroupSize 设置工作组大小。这最终被硬编码到着色器中。

现在我们有了着色器,可以设置3 个缓冲区来存储这些结果。

const numWorkgroups = arrayProd(dispatchCount);
  const numResults = numWorkgroups * numThreadsPerWorkgroup;
  const size = numResults * 4 * 4;  // vec3f * u32
 
  let usage = GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC;
  const workgroupBuffer = device.createBuffer({size, usage});
  const localBuffer = device.createBuffer({size, usage});
  const globalBuffer = device.createBuffer({size, usage});

As we pointed out before, we can not map storage buffers into JavaScript so we need some buffers to we can map. We’ll copy the results from the storage buffers to these mappable result buffers and then read the results.

之前提到过,无法直接将storage 缓冲区映射到 JavaScript,因此需要一些缓冲区来进行映射。会将结果从存储缓冲区复制到这些可映射的结果缓冲区,然后读取结果。

  usage = GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST;
  const workgroupReadBuffer = device.createBuffer({size, usage});
  const localReadBuffer = device.createBuffer({size, usage});
  const globalReadBuffer = device.createBuffer({size, usage});

创建一个绑定组来绑定所有的storage 缓冲区

const bindGroup = device.createBindGroup({
    layout: pipeline.getBindGroupLayout(0),
    entries: [
      { binding: 0, resource: { buffer: workgroupBuffer }},
      { binding: 1, resource: { buffer: localBuffer }},
      { binding: 2, resource: { buffer: globalBuffer }},
    ],
  });

We start an encoder and a compute pass encoder, the same as our previous example, then add the commands to run the compute shader.

启动一个编码器和一个计算通道编码器,与之前的示例相同,然后添加运行计算着色器的命令。

  // Encode commands to do the computation
  const encoder = device.createCommandEncoder({ label: 'compute builtin encoder' });
  const pass = encoder.beginComputePass({ label: 'compute builtin pass' });
 
  pass.setPipeline(pipeline);
  pass.setBindGroup(0, bindGroup);
  pass.dispatchWorkgroups(...dispatchCount);
  pass.end();

We need to copy the results from the storage buffers to the mappable result buffers.

需要将结果从storage 缓冲区复制到可映射结果缓冲区。

  encoder.copyBufferToBuffer(workgroupBuffer, 0, workgroupReadBuffer, 0, size);
  encoder.copyBufferToBuffer(localBuffer, 0, localReadBuffer, 0, size);
  encoder.copyBufferToBuffer(globalBuffer, 0, globalReadBuffer, 0, size);

然后结束编码器并提交命令缓冲区。

 // Finish encoding and submit the commands
  const commandBuffer = encoder.finish();
  device.queue.submit([commandBuffer]);

Like before, to read the results we map the buffers and once they are ready we get typed array views of their contents.

像以前一样,为了读取结果,我们映射了缓冲区,一旦它们准备就绪,就得到了它们内容的类型化数组视图。

   // Read the results
   await Promise.all([
    workgroupReadBuffer.mapAsync(GPUMapMode.READ),
    localReadBuffer.mapAsync(GPUMapMode.READ),
    globalReadBuffer.mapAsync(GPUMapMode.READ),
  ]);
 
  const workgroup = new Uint32Array(workgroupReadBuffer.getMappedRange());
  const local = new Uint32Array(localReadBuffer.getMappedRange());
  const global = new Uint32Array(globalReadBuffer.getMappedRange());

Important: We mapped 3 buffers here and used await Promise.all to wait for them all to be ready to use. You can *NOT just wait on the last buffer. You must wait on all 3 buffers.

重要提示:在这里映射了 3 个缓冲区,并使用 await Promise.all 等待它们都准备好使用。不能不只是等待最后一个缓冲区。您必须等待所有 3 个缓冲区。

最后我们可以打印出来

const get3 = (arr, i) => {
    const off = i * 4;
    return `${arr[off]}, ${arr[off + 1]}, ${arr[off + 2]}`;
  };
 
  for (let i = 0; i < numResults; ++i) {
    if (i % numThreadsPerWorkgroup === 0) {
      log(`\
 ---------------------------------------
 global                 local     global   dispatch: ${i / numThreadsPerWorkgroup}
 invoc.    workgroup    invoc.    invoc.
 index     id           id        id
 ---------------------------------------`);
    }
    log(` ${i.toString().padStart(3)}:      ${get3(workgroup, i)}      ${get3(local, i)}   ${get3(global, i)}`)
  }
}
 
function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = args.join(' ');
  document.body.appendChild(elem);
}

这是结果

---------------------------------------
global                 local     global   dispatch: 0
invoc.    workgroup    invoc.    invoc.
index     id           id        id
---------------------------------------
  0:      0, 0, 0      0, 0, 0   0, 0, 0
  1:      0, 0, 0      1, 0, 0   1, 0, 0
  2:      0, 0, 0      0, 1, 0   0, 1, 0
  3:      0, 0, 0      1, 1, 0   1, 1, 0
  4:      0, 0, 0      0, 2, 0   0, 2, 0
  5:      0, 0, 0      1, 2, 0   1, 2, 0
  6:      0, 0, 0      0, 0, 1   0, 0, 1
  7:      0, 0, 0      1, 0, 1   1, 0, 1
  8:      0, 0, 0      0, 1, 1   0, 1, 1
  9:      0, 0, 0      1, 1, 1   1, 1, 1
 10:      0, 0, 0      0, 2, 1   0, 2, 1
 11:      0, 0, 0      1, 2, 1   1, 2, 1
 12:      0, 0, 0      0, 0, 2   0, 0, 2
 13:      0, 0, 0      1, 0, 2   1, 0, 2
 14:      0, 0, 0      0, 1, 2   0, 1, 2
 15:      0, 0, 0      1, 1, 2   1, 1, 2
 16:      0, 0, 0      0, 2, 2   0, 2, 2
 17:      0, 0, 0      1, 2, 2   1, 2, 2
 18:      0, 0, 0      0, 0, 3   0, 0, 3
 19:      0, 0, 0      1, 0, 3   1, 0, 3
 20:      0, 0, 0      0, 1, 3   0, 1, 3
 21:      0, 0, 0      1, 1, 3   1, 1, 3
 22:      0, 0, 0      0, 2, 3   0, 2, 3
 23:      0, 0, 0      1, 2, 3   1, 2, 3
---------------------------------------
global                 local     global   dispatch: 1
invoc.    workgroup    invoc.    invoc.
index     id           id        id
---------------------------------------
 24:      1, 0, 0      0, 0, 0   2, 0, 0
 25:      1, 0, 0      1, 0, 0   3, 0, 0
 26:      1, 0, 0      0, 1, 0   2, 1, 0
 27:      1, 0, 0      1, 1, 0   3, 1, 0
 28:      1, 0, 0      0, 2, 0   2, 2, 0
 29:      1, 0, 0      1, 2, 0   3, 2, 0
 30:      1, 0, 0      0, 0, 1   2, 0, 1
 31:      1, 0, 0      1, 0, 1   3, 0, 1
 32:      1, 0, 0      0, 1, 1   2, 1, 1
 33:      1, 0, 0      1, 1, 1   3, 1, 1
 34:      1, 0, 0      0, 2, 1   2, 2, 1
 35:      1, 0, 0      1, 2, 1   3, 2, 1
 36:      1, 0, 0      0, 0, 2   2, 0, 2
 37:      1, 0, 0      1, 0, 2   3, 0, 2
 38:      1, 0, 0      0, 1, 2   2, 1, 2
 39:      1, 0, 0      1, 1, 2   3, 1, 2
 40:      1, 0, 0      0, 2, 2   2, 2, 2
 41:      1, 0, 0      1, 2, 2   3, 2, 2
 42:      1, 0, 0      0, 0, 3   2, 0, 3
 43:      1, 0, 0      1, 0, 3   3, 0, 3
 44:      1, 0, 0      0, 1, 3   2, 1, 3
 45:      1, 0, 0      1, 1, 3   3, 1, 3
 46:      1, 0, 0      0, 2, 3   2, 2, 3
 47:      1, 0, 0      1, 2, 3   3, 2, 3
---------------------------------------
global                 local     global   dispatch: 2
invoc.    workgroup    invoc.    invoc.
index     id           id        id
---------------------------------------
 48:      2, 0, 0      0, 0, 0   4, 0, 0
 49:      2, 0, 0      1, 0, 0   5, 0, 0
 50:      2, 0, 0      0, 1, 0   4, 1, 0
 51:      2, 0, 0      1, 1, 0   5, 1, 0
 52:      2, 0, 0      0, 2, 0   4, 2, 0
 53:      2, 0, 0      1, 2, 0   5, 2, 0
 54:      2, 0, 0      0, 0, 1   4, 0, 1
 55:      2, 0, 0      1, 0, 1   5, 0, 1
 56:      2, 0, 0      0, 1, 1   4, 1, 1
 57:      2, 0, 0      1, 1, 1   5, 1, 1
 58:      2, 0, 0      0, 2, 1   4, 2, 1
 59:      2, 0, 0      1, 2, 1   5, 2, 1
 60:      2, 0, 0      0, 0, 2   4, 0, 2
 61:      2, 0, 0      1, 0, 2   5, 0, 2
 62:      2, 0, 0      0, 1, 2   4, 1, 2
 63:      2, 0, 0      1, 1, 2   5, 1, 2
 64:      2, 0, 0      0, 2, 2   4, 2, 2
 65:      2, 0, 0      1, 2, 2   5, 2, 2
 66:      2, 0, 0      0, 0, 3   4, 0, 3
 67:      2, 0, 0      1, 0, 3   5, 0, 3
 68:      2, 0, 0      0, 1, 3   4, 1, 3
 69:      2, 0, 0      1, 1, 3   5, 1, 3
 70:      2, 0, 0      0, 2, 3   4, 2, 3
 71:      2, 0, 0      1, 2, 3   5, 2, 3
---------------------------------------
...

These builtins are generally the only inputs that change per thread of a compute shader for one call to pass.dispatchWorkgroups so to be effective you need to figure out how to use them to design a compute shader function to do what you want, given these …_id builtins as input.

这些内置函数通常是计算着色器的 每个线程为一次调用 pass.dispatchWorkgroups 而改变的唯一输入,因此为了有效,需要弄清楚如何使用它们来设计计算着色器函数来执行想要的操作,给定这些 ..._id 内置作为输入。

工作组大小( Workgroup Size )

你应该建立一个多大的工作组?这个问题经常出现,为什么不总是使用 @workgroup_size(1, 1, 1) ,然后仅通过 pass.dispatchWorkgroups 的参数来决定要运行多少次迭代会更简单。

The reason is multiple threads within a workgroup are faster than individual dispatches.

原因是工作组中的多个线程比单个调度更快。

For one, threads in a workgroup often run in lockstep so running 16 of them is just as fast as running 1.

其一,工作组中的线程通常以锁步(lockstep ,意思为 同步)方式运行,因此运行 16 个线程与运行 1 个线程一样快。

WebGPU 的默认限制如下

  • maxComputeInvocationsPerWorkgroup: 256
  • maxComputeWorkgroupSizeX: 256
  • maxComputeWorkgroupSizeY: 256
  • maxComputeWorkgroupSizeZ: 64

如上,第一个限制 maxComputeInvocationsPerWorkgroup 意味着 @workgroup_size 的3 个参数不能乘以大于256 的数字。换句话说

   @workgroup_size(256, 1, 1)   // ok
   @workgroup_size(128, 2, 1)   // ok
   @workgroup_size(16, 16, 1)   // ok
   @workgroup_size(16, 16, 2)   // bad 16 * 16 * 2 = 512

Unfortunately, the perfect size is GPU dependent and WebGPU can not provide that info. The general advice for WebGPU is to choose a workgroup size of 64 unless you have some specific reason to choose another size. Apparently most GPUs can efficiently run 64 things in lockstep. If you choose a higher number and the GPU can’t do it as a fast path it will chose a slower path. If on the other hand you chose a number below what the GPU can do then you may not get the maximum performance.

不幸的是,完美的大小取决于 GPU,而 WebGPU 无法提供该信息。 WebGPU 的一般建议是选择 64 的工作组大小,除非您有某些特定原因选择其他大小。显然大多数 GPU 可以高效地同步运行 64 个东西。如果您选择一个更高的数字,而 GPU 不能将其作为快速路径执行,它将选择一个较慢的路径。另一方面,如果您选择的数字低于 GPU 的能力,那么您可能无法获得最佳性能。

计算着色器中的竞争 (Races in Compute Shaders)

A common mistake in WebGPU is not handling race conditions. A race condition is where multiple threads are running at the same time and effectively they are in a race for who comes in first or last.

WebGPU 的一个常见错误是没有处理竞争条件。竞争条件是多个线程同时运行并且实际上它们在竞争谁先到或最后。

假设你有这个计算着色器

@group(0) @binding(0) var<storage, read_write> result: array<f32>;
 
@compute @workgroup_size(32) fn computeSomething(
    @builtin(local_invocation_id) local_invocation_id : vec3<u32>,
) {
  result[0] = local_invocation_id.x;
`;

如果这很难理解,这里是类似的 JavaScript

const result = [];
for (let i = 0; i < 32; ++i) {
  result[0] = i;
}

在 JavaScript 的情况下,代码运行后, result[0] 显然是 31。但是在计算着色器的情况下,着色器的所有 32 次迭代都是并行运行的。最后完成的那个是价值将在 result[0] 中的那个。最后运行哪一个是未定义的。

WebGPU 标准:

WebGPU 不提供以下保证:

  • Whether invocations from different workgroups execute concurrently.
    That is, you cannot assume more than one workgroup executes at a
    time.

    来自不同工作组的调用是否同时执行。也就是说,您不能假定同时执行多个工作组。

  • Whether, once invocations from a workgroup begin executing, that
    other workgroups are blocked from execution. That is, you cannot
    assume that only one workgroup executes at a time. While a workgroup
    is executing, the implementation may choose to concurrently execute
    other workgroups as well, or other queued but unblocked work.

    一旦来自一个工作组的调用开始执行,其他工作组是否被阻止执行。也就是说,您不能假设一次只有一个工作组执行。当一个工作组正在执行时,实现可以选择同时执行其他工作组,或其他排队但未阻塞的工作。

  • Whether invocations from one particular workgroup begin executing
    before the invocations of another workgroup. That is, you cannot
    assume that workgroups are launched in a particular order.

    来自一个特定工作组的调用是否在另一个工作组的调用之前开始执行。也就是说,您不能假定工作组以特定顺序启动。

We’ll go over some of the ways to deal with this issue in future examples. For now, our two examples have no race conditions as each iteration of the compute shader does something unaffected by the other iterations.

我们将在以后的示例中介绍一些处理此问题的方法。目前,我们的两个示例没有竞争条件,因为计算着色器的每次迭代都会执行一些不受其他迭代影响的操作。

原文地址

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

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

相关文章

Java安装

Java Downloads | Oracle 一直往下拉 配置环境变量 第二部分&#xff1a;idea旗舰版下载安装配置 1. 字号 file-settings-editor-font-23 &#xff0c;还有菜单字号

边坡变形监测主要用到哪些仪器

在建筑行业中&#xff0c;边坡安全问题一直备受关注&#xff0c;尤其是在地质灾害频发地区。为了预防和控制边坡失稳&#xff0c;降低对人民生命财产的危害&#xff0c;保障工程建设的顺利进行&#xff0c;边坡变形监测工作就显得尤为的重要。边坡变形监测仪器可以实时动态监测…

GSM模块(SIM900)详解

GSM模块&#xff08;SIM900&#xff09; 0. GSM概述1. 常用的GSM模块2. SIM900系列3. STM32使用SIM900系列通信方法AT指令示例代码 0. GSM概述 GSM&#xff08;全球系统移动通信&#xff09;是一种数字移动通信技术&#xff0c;是世界上最常用的移动通信标准之一。它是由欧洲电…

[Unity][疑难杂症]Mac电脑打开工程unity attempt to write a readonly database

报错的根本原因&#xff1a; 你Unity项目所在的硬盘&#xff0c;格式不是MacOS或者APFS格式&#xff0c;我的是ExFAT所以报这个错。 可能出现的问题&#xff1a; 1、删除unity项目中的c#之类的文件就会报错&#xff0c;并且强制退出 2、新项目压根打不开&#xff0c;在生成资源…

【学习笔记】Unity基础(九)【cinemachine基础(body、aim参数详解)】(多fig动图示范)

目录 一 速览1.1 cinemachine下载1.2 官方示例速览1.3 cinemachine定义 二 cinemachine详解2.1 Virtual Camera2.1.1 virtual camera参数通览2.1.2 Status2.1.3 有关Dutch angle2.1.4 Standby Update2.1.5 Transitions 2.2 virtual cameral关键参数详解2.2.1 Body2.2.1.1 Do No…

idea搭建springmvc

Spring MVC是一种基于MVC模式的框架&#xff0c;它是Spring框架的一部分。它提供了一种更简单和更有效的方式来构建Web应用程序。它提供了一系列的功能&#xff0c;可以帮助您更快地构建Web应用程序&#xff0c;如控制器&#xff0c;视图&#xff0c;模型&#xff0c;数据绑定&…

【数据挖掘】时间序列模型处理指南(二)

一、说明 本文是一个系列文章的第二部分&#xff0c;本文将用股票数据进行时间序列分析为例&#xff0c;对时间分析的方法、过程&#xff0c;进行详细阐述。 二、前文章节 在文章第一部分种&#xff1a;【数据挖掘】时间序列模型处理&#xff08;一&#xff09;_无水先生的博客…

Spring Boot中的@EnableWebSocketMessageBroker注解是什么,原理,以及如何使用

Spring Boot中的EnableWebSocketMessageBroker注解是什么&#xff0c;原理&#xff0c;以及如何使用 WebSocket是一种在Web浏览器和Web服务器之间进行双向通信的技术。在传统的HTTP通信中&#xff0c;客户端向服务器发送请求&#xff0c;服务器响应请求&#xff0c;然后关闭连…

黑马Java项目实战-瑞吉外卖-笔记01

视频地址&#xff1a;黑马程序员Java项目实战《瑞吉外卖》&#xff0c;轻松掌握springboot mybatis plus开发核心技术的真java实战项目_哔哩哔哩_bilibili资料下载&#xff1a;百度网盘【黑马程序员-Java瑞吉外卖-企业级项目实战-平台实战开发】 黑马Java项目实战-瑞吉外卖-笔…

PHP表单传值和文件上传:深入解析数据交互与文件处理技术

目录 表单传值 为什么要表单传值&#xff1f; 表单传值的方式 GET传值 POST传值 GET和POST两种传参方式的不同&#xff1a; PHP接受数据的三种方式 PHP处理复选框数据 复选框表单的命名方式 复选框数据的接受形式 复选框数据的常见处理 复选框细节&#xff1a; 文件…

区块链技术如何改变Web3认证的现状?

随着加密货币和区块链技术的迅猛发展&#xff0c;Web3钱包成为了数字经济时代中的重要组成部分。Web3钱包是一种工具&#xff0c;用于存储、管理和交互加密货币及其他数字资产&#xff0c;以及与去中心化应用程序&#xff08;DApp&#xff09;进行交互。它们是实现Web3.0理念的…

chatgpt赋能python:标题:免费的Python编辑器:优秀的编程工具免费获取

标题&#xff1a;免费的Python编辑器&#xff1a;优秀的编程工具免费获取 Python是一种高级编程语言&#xff0c;被广泛应用于数据科学、机器学习、Web开发和科学计算等领域。Python编程简单易学&#xff0c;更重要的是它的灵活性和扩展性&#xff0c;吸引了越来越多的开发者加…

自适应混沌粒子群算法(Matlab)

在上一个博客中作者介绍了标准粒子群及其实现&#xff0c;并给出了许多改进方向&#xff0c;那么从这一期开始就会陆续更新关于粒子群的改进&#xff0c;这一期的改进主要有三个方向&#xff0c;1.混沌初始化粒子群 2 非线性调整惯性权重 3 学习因子动态变化。 下面将对这些改进…

网络犯罪分子在新的代理劫持活动中劫持脆弱的SSH服务器

一个活跃的金融活动针对的是脆弱的SSH服务器&#xff0c;以隐蔽地将它们纳入一个代理网络。 "Akamai研究员Allen West在周四的一份报告中说&#xff1a;"这是一个活跃的活动&#xff0c;攻击者利用SSH进行远程访问&#xff0c;运行恶意脚本&#xff0c;隐蔽地将受害…

正点原子stm32HAL库学习_基础篇_1

单片机简介 STM32芯片分类 ST中文社区网&#xff1a;https://www.stmcu.org.cn/ ST官网&#xff1a;https://www.st.com/content/st_com/en.html STM32命名规则 数据手册 在设计STM32F103 原理图的时候&#xff0c;我们需要用到一个非常重要的文档&#xff1a;STM32F103 数据 …

Tomcat的概念及部署

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、Tomcat的概述1.tomcat 概述2.tomcat 的主要特点 二、Tomcat的核心组件1.核心组件关系图2.核心组件的介绍2.1 Web容器2.2 servlet容器2.3 JSP容器2.4 Tomcat核心…

7、Redis复制(replica)

Redis复制(replica) 是什么&#xff1f; 就是主从复制&#xff0c;master以写为主&#xff0c;Slave以读为主。当master数据变化的时候&#xff0c;自动将新的数据异步同步到其它slave数据库 读写分离 容灾恢复 数据备份 水平扩容支撑高并发 案例演示 架构说明 一个Master两个…

SQL-每日一题【182.查找重复的电子邮箱】

题目 表: Person 编写一个 SQL 查询来报告所有重复的电子邮件。 请注意&#xff0c;可以保证电子邮件字段不为 NULL。 以 任意顺序 返回结果表。 查询结果格式如下例。 示例 1: 解题思路 前置知识 count&#xff08;&#xff09; 1.count(*) &#xff1a;统计所有的行数&a…

KMeans算法实现图像分割

1. Kmeans聚类算法简介 由于具有出色的速度和良好的可扩展性&#xff0c;Kmeans聚类算法算得上是最著名的聚类方法。Kmeans算法是一个重复移动类中心点的过程&#xff0c;把类的中心点&#xff0c;也称重心(centroids)&#xff0c;移动到其包含成员的平均位置&#xff0c;然后重…

数据结构和算法-2023.06.29

斐波那契数列 初衷&#x1f481;&#x1f3fb; 说来也是惭愧&#xff0c;入行这么久了&#xff0c;一直没有仔细去思考为什么去做这个&#xff0c;之前一直游想法去好好学学数据结构和算法&#xff0c;一直苦于是重点学习Docker、K8S、JVM&#xff0c;多线程&#xff0c;或者是…