高效 CUDA 调试:将 NVIDIA Compute Sanitizer 与 NVIDIA 工具扩展结合使用并创建自定义工具

高效 CUDA 调试:将 NVIDIA Compute Sanitizer 与 NVIDIA 工具扩展结合使用并创建自定义工具

NVIDIA Compute Sanitizer 是一款功能强大的工具,可以节省您的时间和精力,同时提高 CUDA 应用程序的可靠性和性能。 在 CUDA 环境中调试代码既具有挑战性又耗时,尤其是在处理数千个线程时。 计算消毒剂可以提供帮助!

在本系列的第一篇文章《高效 CUDA 调试:如何使用 NVIDIA Compute Sanitizer 寻找错误》中,我们讨论了如何开始使用一些 Compute Sanitizer 工具来在调试代码时检查内存泄漏和竞争条件。

在第二篇文章《高效 CUDA 调试:使用 NVIDIA Compute Sanitizer 进行内存初始化和线程同步》中,我们探索了用于检查内存初始化和线程同步的工具。

在这篇文章中,我们重点介绍了 Compute Sanitizer 的一些其他功能,即它与 NVIDIA 工具扩展 (NVTX) 的集成,用于标记代码以方便更直接地使用 Compute Sanitizer。 我们还讨论了 Compute Sanitizer 本身的 API,以便创建更多用于调试 CUDA 应用程序的工具。

在这篇文章中,我们重点介绍了 Compute Sanitizer 的一些其他功能,即它与 NVIDIA 工具扩展 (NVTX) 的集成,用于标记代码以方便更直接地使用 Compute Sanitizer。 我们还讨论了 Compute Sanitizer 本身的 API,以便创建更多用于调试 CUDA 应用程序的工具。

NVIDIA Compute Sanitizer

Compute Sanitizer 是一套工具,可以对代码的功能正确性执行不同类型的检查。 主要有四种工具:

  • memcheck:内存访问错误和泄漏检测。
  • racecheck:共享内存数据访问危险检测工具。
  • initcheck:未初始化设备全局内存访问检测工具。
  • synccheck:线程同步危险检测。

除了这些工具之外,NVIDIA Compute Sanitizer 还具有更多功能:

  • 一个 API,用于创建针对 CUDA 应用程序的清理和跟踪工具
  • 与 NVIDIA 工具扩展 (NVTX) 集成
  • Coredump 支持与 CUDA-GDB 一起使用
  • 用于管理工具输出的抑制功能

将 Compute Sanitizer 与 NVTX 结合使用

NVTX 是一个基于 C 的 API,用于注释程序中的代码范围、事件和资源。 此注释允许在应用程序运行时收集更多信息,这可用于在分析和分析代码时改进数据表示。 Compute Sanitizer 和 NVTX 之间的集成使您能够使用 NVTX 来注释代码,以帮助 Compute Sanitizer 捕获错误。

有关 NVTX 注释的更多信息,请参阅以下帖子:

  • C/C++ 和 NVTX:CUDA 专业提示:使用 NVTX 生成自定义应用程序配置文件时间线
    Python 和 NVTX:NVIDIA 工具扩展 API:用于分析 Python 和 C/C++ 代码的注释工具
    Fortran 和 NVTX:使用 NVTX 自定义 CUDA Fortran 分析

NVTX 内存 API 使 CUDA 程序能够向 Compute Sanitizer 通知内存限制,例如内存池管理或权限限制以及内存标签。

内存池管理

NVTX 与 Compute Sanitizer 集成的第一个示例来自 NVTX 内存 API 的子分配部分。

使用 API,您可以将内存分配注释为内存池。 Compute Sanitizer 知道这些池,并且可以检测实际使用特定分配的哪些部分。 然后,如果在代码执行期间访问内存池的任何未注册部分,则通过 Compute Sanitizer memcheck 工具检测到这些访问。

这是基本内存池的示例 mempool_example.cu。

#include <stdio.h>__global__ void populateMemory(int* chunk) {int i = threadIdx.x + blockDim.x * blockIdx.x;chunk[i] = i;
}int main(int argc, char **argv) {int poolSize   = 4096 * sizeof(int);int numThreads = 63;// int bucketSize = numThreads * sizeof(int); // You need this later ...void *pool;cudaMallocManaged(&pool, poolSize); // Create your memory pool// Assign part of the memory pool to the bucketauto bucket = (int *)pool + 16; // Address of bucket is 16 bytes into the pool// Set values in bucketpopulateMemory<<<1, numThreads>>>(bucket);cudaDeviceSynchronize();printf("After populateMemory 1: bucket 0, 1 ..  62: %d %d .. %d\n", bucket[0], bucket[1], bucket[numThreads-1]);// Set some more values in bucketpopulateMemory<<<1, numThreads + 1>>>(bucket);cudaDeviceSynchronize();printf("After populateMemory 2: bucket 0, 1 ..  63: %d %d .. %d\n", bucket[0], bucket[1], bucket[numThreads]);cudaFree(pool);exit(0);
}

在代码示例中,您创建了一个大小为 4096 个整数的内存池(称为池!)。 然后,您分配该池的一部分(由变量存储桶标记),从距池开头 16 字节的地址开始。

您打算将存储桶设置为具有 numThreads 个元素,在本例中为 63,由变量bucketSize 确定。 然后,您可以使用 GPU 内核 populateMemory 用一些值填充存储桶。 块数设置为 1,线程数设置为 numThreads,这意味着 populateMemory 执行 1×63 次,按预期设置存储桶中的每个值。

但是,您随后尝试通过 populateMemory 内核再次填充存储桶。 这次,您将线程数设置为 numThreads+1 (64)。 您的意图是让存储桶具有 63 个值,但现在您尝试分配 64 个值。但是,这不会导致错误。 例如,您没有访问任何越界内存,因为存储桶所属的实际内存池足够大,可以容纳额外的元素。

编译它并通过 memcheck 运行它,以确认这个潜在的错误没有被发现。 我们在 NVIDIA V100 GPU 上运行,因此我们将 GPU 架构设置为 sm_70。 您可能需要更改此设置,具体取决于您运行的内容。

$ nvcc -o mempool.exe mempool_example.cu -arch=sm_70
$ ./mempool.exe
After populateMemory 1: bucket 0, 1 ..  62: 0 1 .. 62
After populateMemory 2: bucket 0, 1 ..  63: 0 1 .. 63$compute-sanitizer --tool memcheck ./mempool.exe
========= COMPUTE-SANITIZER
After populateMemory 1: bucket 0, 1 ..  62: 0 1 .. 62
After populateMemory 2: bucket 0, 1 ..  63: 0 1 .. 63
========= ERROR SUMMARY: 0 errors

这就是 NVTX API 可以提供帮助的地方。 您可以使用 NVTX 的内存堆寄存器函数 nvtxMemHeapRegister 注册任何 cudaMalloc 内存分配。 这将内存注册为一个堆,代表可以进一步细分为多个区域的内存范围。 您可以通过以下过程在此代码中执行此操作。

首先,完成将 NVTX 与 Compute Sanitizer 结合使用所需的四个步骤。

对于 C 和 C++,NVTX 是一个仅包含头文件的库,没有依赖项,因此您必须获取 NVTX 头文件才能包含。 通常,这些会随您首选的 CUDA 下载一起提供,例如工具包或 HPC SDK。 但是,NVTX 内存 API 相对较新,因此现在可以从 /NVIDIA/NVTX GitHub 存储库获取它。 将来,它将作为工具包的一部分包含在内。

特别是,nvToolsExtMem.h 标头尚无法通过其他方法获得,因此在克隆 NVTX GitHub 分支 dev-mem-api 后检查它是否存在:

$ git clone --branch dev-mem-api https://github.com/NVIDIA/NVTX.git
…
$ ls NVTX/c/include/nvtx3/
nvToolsExtCuda.h    nvToolsExt.h           nvToolsExtMem.h     nvToolsExtSync.h  nvtxDetail
nvToolsExtCudaRt.h  nvToolsExtMemCudaRt.h  nvToolsExtOpenCL.h  nvtx3.hpp         nvtxExtDetail

现在您可以在源代码的开头包含 NVTX 和 NVTX API 头文件:

#include <nvtx3/nvToolsExt.h>
#include <nvtx3/nvToolsExtMem.h>

Compute Sanitizer 要求在任何 NVTX 调用之前初始化 CUDA 运行时。 无论如何,这可能会发生在您的代码中,具体取决于您开始使用 NVTX 的位置,但您可以使用 cudaFree 强制执行它,例如:

// Forces CUDA runtime initialization.
cudaFree(0);

最后,创建一个 NVTX 域。 这些是调用 API 所必需的。 目前,这些域没有特定的功能,但将用于未来的 Compute Sanitizer 版本。

// Create the NVTX domain
auto mynvtxDomain = nvtxDomainCreateA("my-domain");

好的,这就是完成的先决步骤。 现在,使用 NVTX 将池分配注册为内存池或堆:

nvtxMemVirtualRangeDesc_t myPoolRangeDesc = {}; // Descriptor for the// range memory pool
myPoolRangeDesc.size = poolSize; // Size of the range memory pool
myPoolRangeDesc.ptr  = pool;     // Pointer to the pool itselfnvtxMemHeapDesc_t myHeapDesc = {}; // Descriptor for the heapmyHeapDesc.extCompatID = NVTX_EXT_COMPATID_MEM;
myHeapDesc.structSize = sizeof(nvtxMemHeapDesc_t);
myHeapDesc.usage = NVTX_MEM_HEAP_USAGE_TYPE_SUB_ALLOCATOR;
myHeapDesc.type = NVTX_MEM_TYPE_VIRTUAL_ADDRESS;
myHeapDesc.typeSpecificDescSize = sizeof(nvtxMemVirtualRangeDesc_t);
myHeapDesc.typeSpecificDesc = &myPoolRangeDesc;auto mynvtxPool = nvtxMemHeapRegister(mynvtxDomain, &myHeapDesc);

这些步骤已注册该池并将其分配给变量 mynvtxPool。 要在前面的示例中使用它,您现在必须在池中创建一个子分配来代表存储桶。 语法与分配池本身的方式没有什么不同,但这次使用区域描述符而不是堆描述符:

nvtxMemVirtualRangeDesc_t mySubRangeDesc = {}; // Descriptor for the range
mySubRangeDesc.size = bucketSize; // Size of your suballocation (in bytes)
mySubRangeDesc.ptr  = bucket;     // Pointer to the suballocationnvtxMemRegionsRegisterBatch_t myRegionsDesc = {};
myRegionsDesc.extCompatID = NVTX_EXT_COMPATID_MEM;
myRegionsDesc.structSize  = sizeof(nvtxMemRegionsRegisterBatch_t);
myRegionsDesc.regionType  = NVTX_MEM_TYPE_VIRTUAL_ADDRESS;
myRegionsDesc.heap = mynvtxPool; // The heap you registered earlier
myRegionsDesc.regionCount = 1;
myRegionsDesc.regionDescElementSize = sizeof(nvtxMemVirtualRangeDesc_t);
myRegionsDesc.regionDescElements = &mySubRangeDesc;nvtxMemRegionsRegister(mynvtxDomain, &myRegionsDesc);

这既是您的内存池,又是现在已向 NVTX 注册的子分配存储桶。 这意味着 Compute Sanitizer 可以将它们的属性作为其检查的一部分。 现在,看看它是否检测到将存储桶填充到其预期范围之外的错误尝试。

以下是现在具有 NVTX 注册的基本内存池的完整代码示例,包括 NVTX 注册,mempool_nvtx_example.cu

#include <nvtx3/nvToolsExt.h>
#include <nvtx3/nvToolsExtMem.h>#include <stdio.h>__global__ void populateMemory(int* chunk) {int i = threadIdx.x + blockDim.x * blockIdx.x;chunk[i] = i;
}int main(int argc, char **argv) {int poolSize   = 4096 * sizeof(int);int numThreads = 63;int bucketSize = numThreads * sizeof(int);// Forces CUDA runtime initialization.cudaFree(0);// Create the NVTX domainauto mynvtxDomain = nvtxDomainCreateA("my-domain");void *pool;cudaMallocManaged(&pool, poolSize); // Create your memory pool// Register the pool with NVTXnvtxMemVirtualRangeDesc_t myPoolRangeDesc = {}; // Descriptor for the// range memory poolmyPoolRangeDesc.size = poolSize; // Size of the range memory poolmyPoolRangeDesc.ptr  = pool;     // Pointer to the pool itselfnvtxMemHeapDesc_t myHeapDesc = {}; // Descriptor for the heapmyHeapDesc.extCompatID = NVTX_EXT_COMPATID_MEM;myHeapDesc.structSize = sizeof(nvtxMemHeapDesc_t);myHeapDesc.usage = NVTX_MEM_HEAP_USAGE_TYPE_SUB_ALLOCATOR;myHeapDesc.type = NVTX_MEM_TYPE_VIRTUAL_ADDRESS;myHeapDesc.typeSpecificDescSize = sizeof(nvtxMemVirtualRangeDesc_t);myHeapDesc.typeSpecificDesc = &myPoolRangeDesc;auto mynvtxPool = nvtxMemHeapRegister(mynvtxDomain, &myHeapDesc);// Assign part of the memory pool to the bucketauto bucket = (int *)pool + 16; // Address of bucket is 16 bytes into the pool// Register bucket as a suballocated region in NVTXnvtxMemVirtualRangeDesc_t mySubRangeDesc = {}; // Descriptor for the rangemySubRangeDesc.size = bucketSize; // Size of your suballocation (in bytes)mySubRangeDesc.ptr  = bucket;     // Pointer to the suballocationnvtxMemRegionsRegisterBatch_t myRegionsDesc = {};myRegionsDesc.extCompatID = NVTX_EXT_COMPATID_MEM;myRegionsDesc.structSize  = sizeof(nvtxMemRegionsRegisterBatch_t);myRegionsDesc.regionType  = NVTX_MEM_TYPE_VIRTUAL_ADDRESS;myRegionsDesc.heap = mynvtxPool; // The heap you registered earliermyRegionsDesc.regionCount = 1;myRegionsDesc.regionDescElementSize = sizeof(nvtxMemVirtualRangeDesc_t);myRegionsDesc.regionDescElements = &mySubRangeDesc;nvtxMemRegionsRegister(mynvtxDomain, &myRegionsDesc);// Set values in bucketpopulateMemory<<<1, numThreads>>>(bucket);cudaDeviceSynchronize();printf("After populateMemory 1: bucket 0, 1 ..  62: %d %d .. %d\n", bucket[0], bucket[1], bucket[numThreads-1]);// Set some more values in bucketpopulateMemory<<<1, numThreads + 1>>>(bucket);cudaDeviceSynchronize();printf("After populateMemory 2: bucket 0, 1 ..  63: %d %d .. %d\n", bucket[0], bucket[1], bucket[numThreads]);cudaFree(pool);exit(0);
}

编译它并再次通过 Compute Sanitizer 运行它。 编译步骤中的 include 语句应指向您安装 NVTX 标头的位置。

$ nvcc -I ./NVTX/c/include -o mempool_nvtx.exe mempool_nvtx_example.cu -arch=sm_70
$ compute-sanitizer --tool memcheck --destroy-on-device-error=kernel ./mempool_nvtx.exe
========= COMPUTE-SANITIZER
After populateMemory 1: bucket 0, 1 ..  62: 0 1 .. 62
========= Invalid __global__ write of size 4 bytes
=========     at populateMemory(int *)+0x70
=========     by thread (63,0,0) in block (0,0,0)
=========     Address 0x7f2a9800013c is out of bounds
=========     and is 1 bytes after the nearest allocation at 0x7f2a98000040 of size 252 bytes
=========     Saved host backtrace up to driver entry point at kernel launch time
. . .
=========
After populateMemory 2: bucket 0, 1 ..  63: 0 1 .. 0
========= ERROR SUMMARY: 1 error

Compute Sanitizer 确实捕获了写入指定数组末尾的元素的尝试:

Invalid __global__ write of size 4 bytes

现在,如果您有部分内存池需要调整大小甚至销毁怎么办? NVTX 内存 API 还提供了类似的方法来对其 NVTX 注册执行此操作。

要调整大小,请返回之前的池和存储桶示例。 如果您想要将存储桶的大小从 63 个元素调整为 64 个元素,请使用以下代码修改前面的代码示例,也会调整您的 NVTX 注册存储桶的大小以反映这一点:

// Resizing the sub-allocation within the memory pool// You reuse mySubRangeDesc from earliermySubRangeDesc.size = bucketSize + 4; // You want one extra int (4B) elementmySubRangeDesc.ptr  = bucket;nvtxMemRegionsResizeBatch_t myNewRegionsDesc = {};myNewRegionsDesc.extCompatID = NVTX_EXT_COMPATID_MEM;myNewRegionsDesc.structSize = sizeof(mySubRangeDesc);myNewRegionsDesc.regionType = NVTX_MEM_TYPE_VIRTUAL_ADDRESS;myNewRegionsDesc.regionDescCount = 1;myNewRegionsDesc.regionDescElementSize = sizeof(mySubRangeDesc);myNewRegionsDesc.regionDescElements = &mySubRangeDesc;nvtxMemRegionsResize(mynvtxDomain, &myNewRegionsDesc);

正如您所看到的,它与子分配的初始声明类似,但最后使用了函数 nvtxMemRegionsResize。

仔细检查 Compute Sanitizer 是否对现在为调整大小的存储桶分配值的合法尝试感到满意。 在示例代码中两次调用populateMemory之间添加调整大小注册代码,然后编译运行。

$ nvcc -I./NVTX/c/include -o mempool_resize.exe mempool_resize_example.cu -arch=sm_70
$ compute-sanitizer --tool memcheck --destroy-on-device-error=kernel ./mempool_resize.exe
========= COMPUTE-SANITIZER
After populateMemory 1: bucket 0, 1 ..  62: 0 1 .. 62
After populateMemory 2: bucket 0, 1 ..  63: 0 1 .. 63
========= ERROR SUMMARY: 0 errors

希望您能看到类似此示例的内容。 当您调整了子分配注册的大小后,现在不再有关于尝试访问添加到末尾的新元素的投诉。

需要相当多的代码行来注册和调整池和子分配的大小,以便将它们与 NVTX 和 Compute Sanitizer 一起使用。 对于更复杂的代码来说,这可能会变得很麻烦,因此将这些步骤封装到一个单独的类中可能会很有用。 很方便,/NVIDIA/compute-sanitizer-samples GitHub 存储库中提供了这种方法的一个很好的示例,因此这是您自己的代码的一个很好的起点。

还有两个 NVTX API 需要提及:

  • 命名 API:使区域或子分配具有与其关联的 ASCII 名称。 然后,它可以用于在错误报告中通过名称来引用分配,目前支持泄漏和未使用的内存报告。
  • 权限 API:允许将分配访问权限限制为只读或原子等。

Compute Sanitizer API 用于创建您自己的工具

Compute Sanitizer 附带 API,使您能够创建自己的清理和跟踪工具来定位 CUDA 应用程序。 它是一组函数,可用于与 Compute Sanitizer 交互以进行控制和配置、启用或禁用其功能以及访问其结果。

该 API 还为您提供了一种将 Compute Sanitizer 集成到您的开发工作流程中的便捷方法,因为它可以轻松集成到现有的 CUDA 应用程序中。 借助 Compute Sanitizer API,您可以直接利用强大的调试功能并提高 CUDA 应用程序的可靠性和性能。

它由以下子 API 组成:

  • 回调:使您能够在用户代码中注册回调,其中回调可以与相关 CUDA 函数或事件组关联,例如 memcpy 操作或驱动程序函数。 然后订阅者可以使用这些回调,例如用于事件跟踪。
  • 修补:允许加载修补函数并将其插入到 GPU 上执行的设备代码中。 然后,它们可以用作检测点,这意味着每当执行修补事件时都会执行修补函数,例如,设置回调,例如进行内存访问的设备代码。
  • 内存:提供标准 CUDA 内存 API 的替换函数。 可以从 Compute Sanitizer 回调中安全地调用替换,例如使用替换 sanitizerAlloc 而不是 cudaMalloc。

结合起来,这些 API 使您能够将 Compute Sanitizer 功能合并到您自己的工具中。

有关更多信息和一些示例代码,请参阅 NVIDIA Compute Sanitizer API 指南。

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

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

相关文章

超详细的fiddler教程

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Fiddler&#xff08;中文名称&#xff1a;小提琴&#xff09;是…

【算法题】三道题理解算法思想--滑动窗口篇

滑动窗口 本篇文章中会带大家从零基础到学会利用滑动窗口的思想解决算法题&#xff0c;我从力扣上筛选了三道题&#xff0c;难度由浅到深&#xff0c;会附上题目链接以及算法原理和解题代码&#xff0c;希望大家能坚持看完&#xff0c;绝对能有收获&#xff0c;大家有更好的思…

win11 安装SIBR 3dgs

1.安装显卡驱动 下载地址&#xff1a; 官方驱动 | NVIDIA下载适用于 GeForce、TITAN、NVIDIA RTX、数据中心、GRID 等 NVIDIA 产品的新驱动。https://www.nvidia.cn/Download/index.aspx?langcn 2.安装cuda 下载地址&#xff1a;如果无法打开&#xff0c;切换.com为.cn&am…

JavaScript基础练习题之计算数组元素的和与平均值

一、如何使用JavaScript计算数组元素的和与平均值&#xff1f; 二、正确的源程序 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>计算数组元素的和与平均值</title></head><body><h1>计算数组元…

两直线交点算法 C

求两直线交点算法 有中间交点 CD在AB异侧 且AB在CD异侧 AB在CD异侧 但 CD在AB同侧 无中间交点 A B A C A B A D \nobreak AB \times AC \newline AB \times AD ABACABAD 异号 叉乘后相乘小于零 等于零的几种情况 A B C与AB共线 D与AB共线 求交点&#xff0c;可由面积比…

iOS - LLVM的中间代码(IR)

文章目录 iOS - LLVM的中间代码&#xff08;IR&#xff09;1. 转为汇编代码2. 中间代码&#xff08;IR&#xff09;2.1 Objective-C在变为机器代码之前&#xff0c;会被LLVM编译器转换为中间代码&#xff08;Intermediate Representation&#xff09;2.2 可以使用以下命令行指令…

武忠祥《660题》高效刷题包+资料分享

660题的难度书虽然比较难&#xff0c;对于基础的考察比较深入&#xff0c;所以&#xff0c;有没有一种可能&#xff0c;做题太慢&#xff0c;是因为基础不好导致的&#xff01; 所以再继续做下去&#xff0c;就没有什么意义了&#xff0c;因为这就像是用一把钝刀去砍树&#x…

VS2022 使用ClaudiaIDE设置自定义图片背景

ClaudiaIDE的下载 第一步&#xff0c;如下图所示&#xff0c;点击&#xff1a;扩展——管理扩展。 第二步&#xff0c;如下图所示&#xff0c;点击&#xff1a;联机——右上角输入ClaudiaIDE搜索——点击下载。 下载后关闭所有VS窗口&#xff0c;然后等待弹出一个安装窗口&…

小米汽车正式发布:开启智能电动新篇章

随着科技的不断进步&#xff0c;汽车产业正经历着前所未有的变革。智能电动汽车作为这一变革的重要方向&#xff0c;正吸引着越来越多的目光。在这个充满机遇和挑战的时代&#xff0c;小米汽车凭借其卓越的技术实力和深厚的市场底蕴&#xff0c;终于迈出了坚实的一步。今天&…

Exception in thread “main“ com.fasterxml.jackson.databind.JsonMappingException:

问题&#xff1a;jaskson反序列化超出最大长度 Caused by: com.fasterxml.jackson.core.exc.StreamConstraintsException: String length (5043456) exceeds the maximum length (5000000) 场景&#xff1a;前端传递过大base64 原因&#xff1a; jaskon默认已经限制了最大长…

免费|Python|【需求响应】一种新的需求响应机制DR-VCG研究

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序对应文章《Contract Design for Energy Demand Response》&#xff0c;电力系统需求响应&#xff08;DR&#xff09;用来调节用户对电能的需求&#xff0c;即在预测的需求高于电能供应时&#xff0c;希…

chrome 控制台不能粘贴

描述&#xff1a;chrome 控制台不能粘贴 解决方案&#xff1a;setting-Experiments-Filter中输入past&#xff0c;取消勾选

国际伦敦金行情分析中的趋势分析方法

国际伦敦金行情走势复杂多变。近期&#xff0c;金价曾经一度刷新历史的新高点至2222&#xff0c;但就在当天&#xff0c;金价又快速下跌跌超过30美元。不过这么多变的伦敦金行情也为我们的交易创造了空间&#xff0c;有空间就等于有机会&#xff0c;只要我们能够掌握国际伦敦金…

HCIP作业3

第一步 给PC1配置&#xff1a; 先给PC2配置&#xff1a; 第二部 给R1的接口配置ip [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 192.168.1.252 24 [R1-GigabitEthernet0/0/0]int g0/0/1 [R1-GigabitEthernet0/0/1]ip ad 100.1.1.1 24 给R2的接口配置IP [R2]int g0/0/…

Java:接口应用(Comparable接口与比较器)

目录 1.引例2.Comparable接口使用3.Comparable接口的局限性4.使用comparaTo实现排序5.比较器&#xff08;Comparator接口&#xff09; 1.引例 class Student{private String name;private int age;public Student(String name, int age) {this.name name;this.age age;} } p…

#孩子学习编程的目的是什么?#

谢谢今日头条编辑的盛情邀请&#xff0c;对此&#xff0c;我发表自己的一点拙见。 首先&#xff0c;最重要的一点&#xff0c;学习编程可以练习打字&#xff0c;加强拼音输入法的掌握。对于孩子的语文成绩有一定的提高帮助&#xff0c;也为将来走上工作熟练使用电脑输入法打下扎…

Linux的学习之路:3、基础指令(2)

一、echo指令 这个指令在上篇文章我也用了但是忘了说了&#xff0c;这个指令的大概用法就是把后面跟的文本等输出在显示器上&#xff0c;如下代码所示打印的“Hello Linux” [rootVM-24-9-centos ~]# echo "Hello Linux" Hello Linux二、输出重定向与输入重定向 着…

Java实现猜数字游戏:编程入门之旅

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

使用 Yoda 和 ClickHouse 进行实时欺诈检测

背景 Instacart 是北美领先的在线杂货公司,拥有数百万活跃的客户和购物者。在其平台上打击欺诈和滥用行为不仅对于维护一个值得信赖和安全的环境至关重要,也对保持Instacart的财务健康至关重要。在这篇文章中,将介绍了一个欺诈平台——Yoda,解释了为什么我们选择ClickHous…

Linux 反引号、单引号以及双引号的区别

1.单引号—— 单引号中所有的字符包括特殊字符&#xff08;$,,和\&#xff09;都将解释成字符本身而成为普通字符。它不会解析任何变量&#xff0c;元字符&#xff0c;通配符&#xff0c;转义符&#xff0c;只被当作字符串处理。 2.双引号——" 双引号&#xff0c;除了$,…