3.9 EXERCISES

  1. 矩阵加法需要两个输入矩阵A和B,并产生一个输出矩阵C。输出矩阵C的每个元素都是输入矩阵A和B的相应元素的总和,即C[i][j] = A[i][j] + B[i][j]。为了简单起见,我们将只处理元素为单精度浮点数的平方矩阵。编写一个矩阵加法内核和主机stub函数,可以使用四个参数调用:指针到输出矩阵、指针到第一个输入矩阵、指针到第二个输入矩阵以及每个维度中的元素数量。按照以下说明操作:
#include <stdio.h>
#include <cuda_runtime.h>// CUDA内核函数定义
__global__ void matrixAdd(float *C, const float *A, const float *B, int N) {int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;if (row < N && col < N) {int idx = row * N + col;C[idx] = A[idx] + B[idx];}
}int main() {int N = 256; // 矩阵大小为 N x Nsize_t bytes = N * N * sizeof(float);// 分配主机内存float *h_A = (float*)malloc(bytes);float *h_B = (float*)malloc(bytes);float *h_C = (float*)malloc(bytes);// 初始化输入矩阵for (int i = 0; i < N * N; i++) {h_A[i] = static_cast<float>(i);h_B[i] = static_cast<float>(i);}// 分配设备内存float *d_A, *d_B, *d_C;cudaMalloc(&d_A, bytes);cudaMalloc(&d_B, bytes);cudaMalloc(&d_C, bytes);// 复制数据从主机到设备cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);// 设置线程块和网格大小dim3 threadsPerBlock(16, 16);dim3 blocksPerGrid((N + threadsPerBlock.x - 1) / threadsPerBlock.x,(N + threadsPerBlock.y - 1) / threadsPerBlock.y);// 执行内核函数matrixAdd<<<blocksPerGrid, threadsPerBlock>>>(d_C, d_A, d_B, N);// 等待CUDA完成并检查错误cudaDeviceSynchronize();cudaError_t error = cudaGetLastError();if (error != cudaSuccess) {fprintf(stderr, "CUDA error: %s\n", cudaGetErrorString(error));// 清理cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);free(h_A);free(h_B);free(h_C);return -1;}// 将结果复制回主机cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);// 验证结果for (int i = 0; i < N * N; i++) {if (h_C[i] != h_A[i] + h_B[i]) {fprintf(stderr, "Result verification failed at element %d!\n", i);return -1;}}printf("Test PASSED\n");// 清理cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);free(h_A);free(h_B);free(h_C);return 0;
}
这段代码首先定义了一个matrixAdd内核,它计算两个矩阵的和,并将结果存储在输出矩阵中。然后在main函数中,它创建了两个输入矩阵和一个输出矩阵,将它们复制到设备内存中,调用内核函数,并将结果复制回主机内存。最后,它验证结果并清理分配的内存。
  1. 矩阵-向量乘法接受输入矩阵B和向量C,并产生一个输出向量A。输出向量A的每个元素都是输入矩阵B和C的一行的点积,即 A[i] = ∑j B[i][j] + C[j]。为了简单起见,我们将只处理元素为单精度浮点数的平方矩阵。编写一个矩阵向量乘法内核和一个主机存根函数,该函数可以使用四个参数调用:指针到输出矩阵、指针到输入矩阵、指针到输入向量以及每个维度的元素数量。使用一个线程来计算输出矢量元素。
#include <stdio.h>
#include <cuda_runtime.h>// CUDA内核函数定义
__global__ void matrixVectorMultiply(float *A, const float *B, const float *C, int N) {int row = blockIdx.x * blockDim.x + threadIdx.x;if (row < N) {float sum = 0.0f;for (int j = 0; j < N; ++j) {sum += B[row * N + j] * C[j];}A[row] = sum;}
}int main() {int N = 256; // 矩阵大小为 N x Nsize_t sizeMatrix = N * N * sizeof(float);size_t sizeVector = N * sizeof(float);// 分配主机内存float *h_B = (float*)malloc(sizeMatrix);float *h_C = (float*)malloc(sizeVector);float *h_A = (float*)malloc(sizeVector);// 初始化输入矩阵B和向量Cfor (int i = 0; i < N * N; i++) {h_B[i] = static_cast<float>(i);}for (int i = 0; i < N; i++) {h_C[i] = static_cast<float>(i);}// 分配设备内存float *d_B, *d_C, *d_A;cudaMalloc(&d_B, sizeMatrix);cudaMalloc(&d_C, sizeVector);cudaMalloc(&d_A, sizeVector);// 复制数据从主机到设备cudaMemcpy(d_B, h_B, sizeMatrix, cudaMemcpyHostToDevice);cudaMemcpy(d_C, h_C, sizeVector, cudaMemcpyHostToDevice);// 设置线程块和网格大小int threadsPerBlock = 256;int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;// 执行内核函数matrixVectorMultiply<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, N);// 等待CUDA完成并检查错误cudaDeviceSynchronize();cudaError_t error = cudaGetLastError();if (error != cudaSuccess) {fprintf(stderr, "CUDA error: %s\n", cudaGetErrorString(error));// 清理cudaFree(d_B);cudaFree(d_C);cudaFree(d_A);free(h_B);free(h_C);free(h_A);return -1;}// 将结果复制回主机cudaMemcpy(h_A, d_A, sizeVector, cudaMemcpyDeviceToHost);// 验证结果(这里跳过了验证步骤)// 清理cudaFree(d_B);cudaFree(d_C);cudaFree(d_A);free(h_B);free(h_C);free(h_A);return 0;
}
  1. 如果CUDA设备的SM最多可以占用1536个线程和最多4个线程块。以下哪个块配置会导致SM中线程数量最多?
    在这里插入图片描述
    答:C 。3*512

  2. 对于向量加法,假设向量长度为2000,每个线程计算一个输出元素,线程块大小为512个线程。网格中将有多少个线程?
    在这里插入图片描述
    答:2048

  3. ***关于上一个问题,由于矢量长度的边界检查,您预计有多少warp发散?(存疑)
    在这里插入图片描述
    答:A.1

因为即便在最后一个线程块中有一些线程不执行实际的向量加法操作,所有的线程都会执行相同的边界检查逻辑,因此没有warp发散。

  1. 您需要编写一个在大小为400 x 900像素的图像上操作的kernel。您想为每个像素分配一个线程。您希望您的线程块是正方形的,并使用设备上每个块的最大线程数(您的设备具有计算能力3.0)。您将如何选择内核的网格尺寸和块尺寸?

对于具有计算能力3.0的NVIDIA GPU,每个线程块最多可以有1024个线程。由于您想要使用正方形的线程块,因此您应该选择一个线程块的尺寸,其边长是1024的平方根。1024的平方根是32,所以您可以选择一个线程块的尺寸为32x32,这样每个块就有 32 × 32 = 1024 32 \times 32 = 1024 32×32=1024个线程,这是最大线程数。
对于一个400x900像素的图像,您需要足够的块来覆盖所有的像素。通过将图像的尺寸除以线程块的尺寸,您可以得到网格的尺寸:

  • 对于宽度(900像素),您需要 ⌈ 900 32 ⌉ = 29 \lceil \frac{900}{32} \rceil = 29 32900=29 个块(因为900不能被32整除,所以您需要向上取整)。
  • 对于高度(400像素),您需要 ⌈ 400 32 ⌉ = 13 \lceil \frac{400}{32} \rceil = 13 32400=13 个块。
    因此,您的网格尺寸将是 29x13 个块,这确保了您有足够的线程块来为每个像素分配一个线程。需要注意的是,这将导致一些线程是多余的,因为29x32 = 928和13x32 = 416,而图像只有900x400。这意味着在宽度方向上将有28个线程和在高度方向上将有16个线程不会对应于图像中的任何像素。在kernel代码中,您需要添加适当的边界检查来避免这些线程进行越界操作。
    综上所述,您的内核配置应该如下:
  • 线程块尺寸(blockDim): (32, 32)
  • 网格尺寸(gridDim): (29, 13)
  1. 关于上一个问题,您希望有多少个空闲线程?

为了确定有多少个空闲线程,我们需要计算网格中所有线程的总数并减去图像中像素的总数。

我们已经决定使用一个网格尺寸为29x13的线程块,每个线程块的尺寸为32x32。所以,网格中总共的线程数是:

总线程数 = 网格宽度 × 网格高度 × 块宽度 × 块高度 \text{总线程数} = \text{网格宽度} \times \text{网格高度} \times \text{块宽度} \times \text{块高度} 总线程数=网格宽度×网格高度×块宽度×块高度

总线程数 = 29 × 13 × 32 × 32 \text{总线程数} = 29 \times 13 \times 32 \times 32 总线程数=29×13×32×32

总线程数 = 387 , 072 \text{总线程数} = 387,072 总线程数=387,072

图像中实际的像素总数是:

像素总数 = 图像宽度 × 图像高度 = 400 × 900 = 360 , 000 \text{像素总数} = \text{图像宽度} \times \text{图像高度} = 400 \times 900 = 360,000 像素总数=图像宽度×图像高度=400×900=360,000

因此,空闲线程的总数是:

空闲线程数 = 总线程数 − 像素总数 \text{空闲线程数} = \text{总线程数} - \text{像素总数} 空闲线程数=总线程数像素总数

空闲线程数 = 387 , 072 − 360 , 000 = 27 , 072 \text{空闲线程数} = 387,072 - 360,000 = 27,072 空闲线程数=387,072360,000=27,072

所以,将会有27,072个空闲线程。这些线程应该在kernel代码中通过边界检查来处理,以确保它们不会执行任何对应于图像外部的操作。

  1. 考虑一个假设的块,在到达block之前,有8个线程执行一段代码。线程需要以下时间(以微秒为单位)来执行这些部分:2.0、2.3、3.0、2.8、2.4、1.9、2.6和2.9,其余时间等待障碍。等待屏障的线程总执行时间的百分比是多少?
    答:在CUDA中,一个线程块中的所有线程在继续执行之前必须到达同步点(屏障)。这意味着所有线程必须等待直到块中最慢的线程完成其执行。在这个假设的情况下,最慢的线程需要3.0微秒来执行它的部分。

对于每个线程,等待时间是最慢线程的执行时间减去该线程的执行时间。我们需要计算每个线程的等待时间,然后将它们加起来得到总等待时间,最后计算这个总等待时间占所有线程执行时间总和的百分比。

让我们先计算总等待时间:

  • 线程1的等待时间: 3.0 − 2.0 = 1.0 3.0 - 2.0 = 1.0 3.02.0=1.0 微秒
  • 线程2的等待时间: 3.0 − 2.3 = 0.7 3.0 - 2.3 = 0.7 3.02.3=0.7 微秒
  • 线程3的等待时间: 3.0 − 3.0 = 0.0 3.0 - 3.0 = 0.0 3.03.0=0.0 微秒(最慢的线程,没有等待时间)
  • 线程4的等待时间: 3.0 − 2.8 = 0.2 3.0 - 2.8 = 0.2 3.02.8=0.2 微秒
  • 线程5的等待时间: 3.0 − 2.4 = 0.6 3.0 - 2.4 = 0.6 3.02.4=0.6 微秒
  • 线程6的等待时间: 3.0 − 1.9 = 1.1 3.0 - 1.9 = 1.1 3.01.9=1.1 微秒
  • 线程7的等待时间: 3.0 − 2.6 = 0.4 3.0 - 2.6 = 0.4 3.02.6=0.4 微秒
  • 线程8的等待时间: 3.0 − 2.9 = 0.1 3.0 - 2.9 = 0.1 3.02.9=0.1 微秒

总等待时间是所有线程等待时间的总和:

总等待时间 = 1.0 + 0.7 + 0.0 + 0.2 + 0.6 + 1.1 + 0.4 + 0.1 = 4.1 微秒 \text{总等待时间} = 1.0 + 0.7 + 0.0 + 0.2 + 0.6 + 1.1 + 0.4 + 0.1 = 4.1 \text{ 微秒} 总等待时间=1.0+0.7+0.0+0.2+0.6+1.1+0.4+0.1=4.1 微秒

总执行时间是所有线程执行时间的总和:

总执行时间 = 2.0 + 2.3 + 3.0 + 2.8 + 2.4 + 1.9 + 2.6 + 2.9 = 19.9 微秒 \text{总执行时间} = 2.0 + 2.3 + 3.0 + 2.8 + 2.4 + 1.9 + 2.6 + 2.9 = 19.9 \text{ 微秒} 总执行时间=2.0+2.3+3.0+2.8+2.4+1.9+2.6+2.9=19.9 微秒

等待时间占总执行时间的百分比是:

百分比 = ( 总等待时间 总执行时间 ) × 100 \text{百分比} = \left( \frac{\text{总等待时间}}{\text{总执行时间}} \right) \times 100 百分比=(总执行时间总等待时间)×100

百分比 = ( 4.1 19.9 ) × 100 ≈ 20.6 % \text{百分比} = \left( \frac{4.1}{19.9} \right) \times 100 \approx 20.6\% 百分比=(19.94.1)×10020.6%

因此,等待屏障的线程总执行时间的百分比大约是20.6%。

  1. 指出每个多处理器可以进行以下哪些分配。在不可能的情况下,请指出限制因素(s)。
    在这里插入图片描述
    好的,我们来看看每个多处理器可以进行的分配情况:

A. 在具有计算能力1.0的设备上,8个块,每个块128个线程

  • 计算能力1.0的设备每个多处理器的最大线程数为768,8个块乘以128个线程等于1024个线程,超出了最大线程数的限制。因此,这种分配是不可能的,限制因素是多处理器的最大线程数。

B. 在具有计算能力1.2的设备上,8个块,每个块128个线程

  • 计算能力1.2的设备每个多处理器的最大线程数为1024,8个块乘以128个线程等于1024个线程,没有超出限制。因此,这种分配是可能的。

C. 在具有计算能力3.0的设备上,8个块,每个块128个线程

  • 计算能力3.0的设备每个多处理器的最大线程数为2048,8个块乘以128个线程等于1024个线程,没有超出限制。因此,这种分配是可能的。

D. 在具有计算能力1.0的设备上,16个块,每个块64个线程

  • 计算能力1.0的设备每个多处理器的最大线程数为768,而16个块乘以64个线程等于1024个线程,超出了最大线程数的限制。此外,计算能力1.0的设备每个多处理器的最大块数为8,这里的16个块也超出了限制。因此,这种分配是不可能的,限制因素是多处理器的最大线程数和最大块数。

E. 在具有计算能力1.2的设备上,16个块,每个块64个线程

  • 计算能力1.2的设备每个多处理器的最大线程数为1024,而16个块乘以64个线程等于1024个线程,没有超出线程数限制。但是,计算能力1.2的设备每个多处理器的最大块数为8,这里的16个块超出了限制。因此,这种分配是不可能的,限制因素是多处理器的最大块数。

F. 在具有计算能力3.0的设备上,16个块,每个块64个线程

  • 计算能力3.0的设备每个多处理器的最大线程数为2048,而16个块乘以64个线程等于1024个线程,没有超出限制。计算能力3.0的设备每个多处理器的最大块数也足够容纳16个块。因此,这种分配是可能的。
  1. 一位CUDA程序员说,如果他们启动一个每个块中只有32个线程的内核,他们可以在需要障碍同步的地方省略__syncthreads()指令。你觉得这是个好主意吗?解释。
    答:不,这不是一个好主意。

__syncthreads()是CUDA编程中的一个同步屏障,它确保块内的所有线程在继续执行之前都达到这一点。这是为了避免竞争条件和数据不一致,尤其是当多个线程需要读写共享内存或者依赖于其他线程的计算结果时。

即使每个块中只有32个线程,也不能保证省略__syncthreads()指令不会导致问题。CUDA架构是SIMT(Single Instruction, Multiple Thread)架构,这意味着一组线程(称为一个线程束)将以锁步方式执行相同的指令。对于NVIDIA的CUDA架构,一个线程束通常包含32个线程,这也是所谓的warp的大小。

当一个块恰好等于一个warp的大小时,人们可能会认为每个warp内的线程是同时执行的,因此不需要同步。然而,这种假设是有风险的,因为:

  1. 即使在一个warp内部,线程执行的顺序和时间仍然是不确定的,尤其是当有分支指令时。
  2. 如果内核中的操作依赖于共享内存或全局内存的读写顺序,省略同步仍然可能导致错误的结果。
  3. 在未来的硬件或软件更新中,warp的行为可能会改变,这使得省略__syncthreads()可能会导致代码的可移植性和可维护性问题。

因此,除非代码的逻辑确保了不同线程之间绝对不需要同步(例如,每个线程使用独立的内存位置,且不依赖于其他线程的计算结果),否则省略__syncthreads()是不安全的。通常,最好是遵循标准的同步做法,以确保代码的正确性和健壮性。

  1. 一位学生提到,他能够通过使用32×32线程块的tile矩阵乘法代码来乘以两个1024×1024矩阵。他正在使用CUDA设备,每个块最多允许512个线程,每个SM最多允许8个块。他进一步提到,线程块中的每个线程都计算结果矩阵的一个元素。你会有什么反应,为什么?

答:
如果该学生声称他们使用32×32线程块来执行1024×1024矩阵的乘法,并且每个线程块中的每个线程都计算结果矩阵的一个元素,那么他们的配置与CUDA设备的硬件限制不符。具体来说,他们的线程块设置超过了每个块允许的最大线程数,因为32×32等于1024,而设备的限制是每个块最多512个线程。

这里的主要问题是,CUDA内核在执行时必须遵守硬件的限制。如果超出了这些限制,CUDA程序将无法成功执行,或者会得到错误的结果。由于CUDA内核的这个限制是由硬件决定的,因此程序员必须设计他们的线程块以适应这些限制。

因此,我的反应将是指出学生的配置错误,并建议他们重新设计线程块的尺寸。为了适应每个块最多512个线程的限制,他们可以选择如下配置之一:

  • 使用16×32线程块,每块有512个线程。
  • 使用32×16线程块,每块有512个线程。
  • 使用其他任何乘积不超过512的线程维度配置。

此外,由于每个SM允许最多8个块,这意味着在任何给定时刻,一个SM可以同时处理的块的数量有限。这个信息对于理解内核如何在多个SM上调度是有用的,但它并不影响线程块大小的选择,只要线程块的大小遵守了每个块的最大线程数限制。

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

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

相关文章

强化学习的数学原理学习笔记 - RL基础知识

文章目录 Roadmap&#x1f7e1;基础概念贝尔曼方程&#xff08;Bellman Equation&#xff09;基本形式矩阵-向量形式迭代求解状态值 vs. 动作值 &#x1f7e1;贝尔曼最优方程&#xff08;Bellman Optimality Equation&#xff0c;BOE&#xff09;基本形式迭代求解 本系列文章介…

DSP2335的时钟PLL配置

PLL模块框图 xclkin是直接进来的外部时钟&#xff1b; 而下面的是振荡器&#xff08;晶振出来&#xff09;的时钟 PLLSTS 锁相环状态寄存器 PLLCR 锁相环控制寄存器 PLLSTS【oscoff】 决定着外部时钟的输入 PLLSTS【plloff】 锁相器关闭位 0使能PLL 锁相环控制寄存器…

Hive 的 安装与部署

目录 1 安装 MySql2 安装 Hive3 Hive 元数据配置到 MySql4 启动 Hive Hive 官网 1 安装 MySql 为什么需要安装 MySql? 原因在于Hive 默认使用的元数据库为 derby&#xff0c;开启 Hive 之后就会占用元数据库&#xff0c;且不与其他客户端共享数据&#xff0c;如果想多窗口操作…

【Java集合篇】HashMap 是如何扩容的

HashMap 是如何扩容的 ✔️ 为什么需要扩容?✔️ 桶元素重新映射✔️链表重新链接✔️ 取消树化✔️拓展知识仓✔️除了rehash之外&#xff0c;哪些操作也会将树会退化成链表? ✔️ 为什么需要扩容? HashMap在Java等编程语言中被广泛使用&#xff0c;用于存储键值对数据。Ha…

【QML COOK】- 001-添加资源文件

1. 下图为要添加的资源文件 2. 将资源文件放置在工程目录中 我放在【Resources/Images】下&#xff0c;你随意 3. 添加qrc类型文件 文件->New File... 选择 Qt->Qt Resource File 填好文件名。我填“Images”你随意 出现名为“Images.qrc”的qrc类型文件 4. 添加资源文…

Ansible:简单、快速、安全、最强大的 IT 自动化系统 | 开源日报 No.140

ansible/ansible Stars: 59.6k License: GPL-3.0 Ansible 是一个极其简单的 IT 自动化系统&#xff0c;它处理配置管理、应用部署、云提供、临时任务执行、网络自动化和多节点编排。Ansible 使得像零停机滚动更新与负载均衡器一样复杂的更改变得容易。主要功能包括&#xff1…

【MATLAB第89期】基于MATLAB的差分自回归滑动平均模型ARIMA时间序列预测模型含预测未来

【MATLAB第89期】基于MATLAB的差分自回归滑动平均模型ARIMA时间序列预测模型含预测未来 往期文章 【MATLAB第82期】基于MATLAB的季节性差分自回归滑动平均模型SARIMA时间序列预测模型含预测未来 一、模型介绍 1、模型简介 差分自回归移动平均模型&#xff08;Autoregressiv…

【BIAI】Lecture 5 - Auditory system

Lecture 5 - Auditory system 专业术语 auditory system 听觉系统 pinna 耳廓 auditory canal 耳道 tympanic membrane 鼓膜 cochlea 耳蜗 ossicles 听骨 auditory-vestibular nerve 前庭神经 oval window 椭圆窗 attenuation reflex 衰减反射 tensor tympani muscle 鼓膜张肌…

网络安全与IP地址:构建数字世界的前沿堡垒

网络安全是当今数字社会中不可忽视的挑战之一。而IP地址&#xff0c;作为互联网通信的基础协议&#xff0c;既是数字化时代的桥梁&#xff0c;也是网络安全的关键节点。本文将剖析IP地址在网络安全领域的作用&#xff0c;以及如何利用其特性建立有效的网络安全策略。 IP地址&a…

华为三层交换机通 过VLANIF虚拟接口实现跨VLAN通信

S1配置 vlan batch 2 to 3interface Vlanif2ip address 192.168.2.254 255.255.255.0interface Vlanif3ip address 192.168.3.254 255.255.255.0interface GigabitEthernet0/0/2port link-type accessport default vlan 2interface GigabitEthernet0/0/3port link-type access…

如何进行sql优化?

在日常工作中都避免不了要和各种SQL语句打交道&#xff0c;无论是开发还是后期维护&#xff0c;一条执行效率高的SQL语句都会对系统性能产生巨大影响。那么&#xff0c;如何进行有效的SQL优化呢&#xff1f;下面将为大家深入浅出地讲解SQL优化的各个方面&#xff1a; 1、了解数…

社科院与美国杜兰大学金融管理硕士项目——勇当开路先锋,争做事业闯将

随着金融行业的不断发展&#xff0c;在职金融人员面临着越来越多的机遇和挑战。在这个充满变革的时代&#xff0c;金融人员需要具备开拓进取的精神&#xff0c;勇当开路先锋&#xff0c;争做事业闯将。只有这样&#xff0c;才能在激烈的竞争中立于不败之地&#xff0c;为企业创…

算法32:针对算法31货币问题进行扩展,并对从左往右模型进行总结

本算法是在算法31的基础之上进行推理总结的&#xff0c;因此&#xff0c;在看本章之前&#xff0c;必须先去了解算法31&#xff0c;否则会觉得莫名其妙。 算法31的推理过程&#xff1a; 如果 x y1 y2 y3 y4 y5 y6. x1 y2 y3 y4 y5 y6 那么 x y1 x1. 根据以…

Codeforces Round 911 C. Anji‘s Binary Tree

原题&#xff1a; C. Anji’s Binary Tree time limit per test 2.5 seconds memory limit per test 256 megabytes input standard input output standard output Keksic keeps getting left on seen by Anji. Through a mutual friend, he’s figured out that Anji really …

ECharts 图表简单示例,中国地图

目录 ECharts官网链接: [ECharts](https://echarts.apache.org/zh/index.html)在项目中引入 Apache ECharts柱状图折线图饼图仪表盘中国地图完整示例代码 ECharts官网链接: ECharts 在项目中引入 Apache ECharts <!DOCTYPE html> <html><head><meta char…

JavaWeb——后端AOP面向特定方法编程

七、AOP 1. 概述 AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程、面向方法编程&#xff0c;其实就是面向特定方法编程 场景&#xff1a; 案例部分功能运行较慢&#xff0c;定位执行耗时较长的业务方法&#xff0c;此时需要统计每个业务…

2024年数学建模美赛能用chatGPT之类的AI吗?官方给了明确规定!

这两年chatGPT等大语言模型火了&#xff0c;能对话&#xff0c;自然也能回答数学建模方面的问题。 那美赛能不能用这些AI呢&#xff1f;2024年美赛官方对chatGPT等的使用做出了明确的规定&#xff08;其中的VI. Contest Instructions部分&#xff09;&#xff1a; https://ww…

JavaScript高级程序设计读书记录(六):定型数组,Map

1. 定型数组 定型数组&#xff08;typed array&#xff09;是 ECMAScript 新增的结构&#xff0c;目的是提升向原生库传输数据的效率。实际上&#xff0c;JavaScript 并没有“TypedArray”类型&#xff0c;它所指的其实是一种特殊的包含数值类型的数组。 1.1 历史 随着浏览器…

LaTex引用字体变色

使用下面这条语句进行修改。 ‘citecolor’改变参考文献颜色&#xff0c; ‘linkcolor’改变图标公式引用的颜色&#xff0c; ‘urlcolor’ 文本网站超链接颜色。 \usepackage[colorlinks,bookmarksopen,bookmarksnumbered,citecolorblue, linkcolorblue, urlcolorblue]{hyper…

杨中科 ASP.NET Core前后端分离开发

一、 前后端分离 1、传统MVC开发模式: 前后端的代码被放到同一个项目中&#xff0c;前端人员负责编写页面的模板&#xff0c;而后端开发人员负责编写控制器和模型的代码并且“套模板”。 缺点: 互相依赖&#xff0c;耦合性强&#xff0c;责任划分不清。 2、主流的“前后端分离…