CUDA编程 - 用向量化访存优化 elementwise 核函数 - 学习记录

Cuda elementwise

  • 一、简介
    • 1.1、ElementWise
    • 1.2、 float4 - 向量化访存
  • 二、实践
    • 2.1、如何使用向量化访存
    • 2.2、Cuda elementwise - Add
    • 2.3、Cuda elementwise - Sigmoid
      • 2.3.1、简单的 Sigmoid 函数
      • 2.3.2、ElementWise Sigmoid+ float4(向量化访存)
    • 2.4、Cuda elementwise - relu
      • 2.3.1、简单的 relu 函数
      • 2.3.2、ElementWise relu + float4(向量化访存)
  • 三、不能使用向量化访存的情况
    • Cuda elementwise - Histogram(直方图)
  • 四、完整代码
    • 4.1、sigmoid.cu
    • 4.2、relu.cu
  • 4.3、histogram.cu

一、简介

1.1、ElementWise

Element-wise操作是最基础,最简单的一种核函数的类型,它的计算特点很符合GPU的工作方式:对于每个元素单独做一个算术操作,然后直接输出。虽然简单,但深度学习领域,有很多算子都是这个类型。常见的有Add,Mul,Concat,各种激活Sigmoid,Relu及它们的变体,归一化里的BatchNorm都属于这个类型。

1.2、 float4 - 向量化访存

所谓向量化访存,就是一次性读 4 个 float,而不是单单 1 个

要点:

  • 小数据规模情况下,可以不考虑向量化访存的优化方式
  • 大规模数据情况下,考虑使用向量化访存,且 最好是缩小grid的维度为原来的1/4,避免影响Occupancy
  • float4 向量化访存只对数据规模大的时候有加速效果,数据规模小的时候没有加速效果

float4 的性能提升主要在于访存指令减少了(同样的数据规模,以前需要4条指令,现在只需1/4的指令),指令cache里就能存下更多指令,提高指令cache的命中率。

判断是否用上了向量化访存,是在 nsight compute 看生成的SASS代码里会有没有 LDG.E.128 Rx, [Rx.64]或 STG.E.128 [R6.64], Rx这些指令的存在。有则向量化成功,没有则向量化失败。

在这里插入图片描述

官方参考链接1
官方参考链接2

二、实践

2.1、如何使用向量化访存

c :

#define FLOAT4(value)  *(float4*)(&(value))

宏解释:

对于一个值,先对他取地址,然后再把这个地址解释成 float4
对于这个 float4的指针,对它再取一个值
这样编译器就可以一次读四个 float

c++ :

#define FLOAT4(value) (reinterpret_cast<float4*>(&(value))[0])

将核函数写成 float4 的形式的时候,首先要先使用宏定义,其次要注意线程数的变化。

线程数变化原因:因为一个线程可以处理 4个float了,所以要减少 四倍的线程。 (具体看下面的例子)

2.2、Cuda elementwise - Add

参考链接

2.3、Cuda elementwise - Sigmoid

2.3.1、简单的 Sigmoid 函数

a: Nx1, b: Nx1, c: Nx1, c = sigmoid(a, b)

//sigmoid<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, N)
//
__global__ void sigmoid(float* x, float* y, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N) y[idx] = 1.0f / (1.0f + expf(-x[idx]));
}

2.3.2、ElementWise Sigmoid+ float4(向量化访存)

Sigmoid x: N, y: N y=1/(1+exp(-x)) float4

__global__ void sigmoid_vec4(float4* x, float4* y, int N) {int idx = (blockIdx.x * blockDim.x + threadIdx.x) * 4;if (idx < N) {float4 tmp_x = FLOAT4(x[idx]);float4 tmp_y;tmp_y.x = 1.0f / (1.0f + expf(-tmp_x.x));tmp_y.y = 1.0f / (1.0f + expf(-tmp_x.y));tmp_y.z = 1.0f / (1.0f + expf(-tmp_x.z));tmp_y.w = 1.0f / (1.0f + expf(-tmp_x.w));FLOAT4(y[idx]) = tmp_y;}
}

2.4、Cuda elementwise - relu

2.3.1、简单的 relu 函数

Relu x: N, y: N y=max(0,x)

__global__ void relu(float* x, float* y, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N) y[idx] = fmaxf(0.0f, x[idx]);
}

2.3.2、ElementWise relu + float4(向量化访存)

Relu x: N, y: N y=max(0,x) float4

__global__ void relu_float4(float* x, float* y, int N) {int idx = (blockIdx.x * blockDim.x + threadIdx.x) * 4;if (idx < N) {float4 tmp_x = FLOAT4(x[idx]);float4 tmp_y;tmp_y.x = fmaxf(0.0f, tmp_x.x);tmp_y.y = fmaxf(0.0f, tmp_x.y);tmp_y.z = fmaxf(0.0f, tmp_x.z);tmp_y.w = fmaxf(0.0f, tmp_x.w);FLOAT4(y[idx]) = tmp_y;}
}

三、不能使用向量化访存的情况

Cuda elementwise - Histogram(直方图)

x[i] = i-- -- - -- - - -
- - - - -

goal: 统计每个元素出现的次数

①、简单的 Histogram函数

x: Nx1, y: count histogram

__global__ void histogram(int* x, int* y, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N) atomicAdd(&(y[x[idx]]), 1);
}

其中 int atomicAdd(int* address, int val);

1、atomicAdd 是 CUDA 中的一个原子加函数,用于实现在多个线程同时修改同一个全局变量的情况下,保证数据一致性和正确性
2、该函数会将原始值和 val 相加,并将结果存储在 address 所指向的内存位置,同时返回原始值
3、当多个线程同时调用 atomicAdd 函数时,CUDA 会自动为它们创建一个硬件级的同步访问机制,从而避免了数据竞争和数据不一致性的问题。

参考链接

②、ElementWise Histogram + float4(向量化访存)

__global__ void histogram_float4(int* x, int* y, int N) {int idx = 4 * (blockIdx.x * blockDim.x + threadIdx.x);if (idx < N) {int4 tmp_y = INT4(x[idx]);atomicAdd(&(y[tmp_y.x]), 1);atomicAdd(&(y[tmp_y.y]), 1);atomicAdd(&(y[tmp_y.z]), 1);atomicAdd(&(y[tmp_y.w]), 1);}
}

运行 histogram_float4 后发现比 histogram 还慢。为什么呢?

1、nvidia 官方回答: Can float4 be used for atomicAdd efficiently?
2、histogram的写入(load)没有向量化指令,只有读取(load)向量化。
3、使用 nsight compute 看生成的SASS,只有 LDG ,没有STG
在这里插入图片描述
4、在一个warp里相邻线程对全局内存y的访问没有合并( 因为在一个warp里面不同线程对全局内存是跳着访问的 )

四、完整代码

4.1、sigmoid.cu

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <vector>
#include<assert.h>
#include <algorithm>
#include <cublas_v2.h>
#include <cuda_runtime.h>#define FLOAT4(value)  *(float4*)(&(value))#define checkCudaErrors(func)               \
{                                   \cudaError_t e = (func);         \if(e != cudaSuccess)                                        \printf ("%s %d CUDA: %s\n", __FILE__,  __LINE__, cudaGetErrorString(e));        \
}__global__ void relu(float* x, float* y, int N){int idx = blockIdx.x * blockDim.x + threadIdx.x;if(idx < N) y[idx] = fmaxf(0.0f, x[idx]);
}__global__ void relu_float4(float* x, float* y, int N){int idx = (blockIdx.x * blockDim.x + threadIdx.x) * 4;if(idx < N){float4 tmp_x = FLOAT4(x[idx]);float4 tmp_y;tmp_y.x = fmaxf(0.0f, tmp_x.x);tmp_y.y = fmaxf(0.0f, tmp_x.y);tmp_y.z = fmaxf(0.0f, tmp_x.z);tmp_y.w = fmaxf(0.0f, tmp_x.w);FLOAT4(y[idx]) = tmp_y;}
}//nvcc -o sigmoid sigmoid.cu && ./sigmoid
//sigmoid<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, N)
//a: Nx1, b: Nx1, c: Nx1, c = sigmoid(a, b)
__global__ void sigmoid(float* a, float* b, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N) b[idx] = 1.0f / (1.0f + expf(-a[idx]));
}__global__ void sigmoid_float4(float* a, float* b, int N)
{int idx = (blockDim.x * blockIdx.x + threadIdx.x) * 4;if(idx < N){float4 tmp_a = FLOAT4(a[idx]);float4 tmp_b;tmp_b.x = 1.0f /(1.0f + expf(-tmp_a.x));tmp_b.y = 1.0f /(1.0f + expf(-tmp_a.y));tmp_b.z = 1.0f /(1.0f + expf(-tmp_a.z));tmp_b.w = 1.0f /(1.0f + expf(-tmp_a.w));FLOAT4(b[idx]) = tmp_b;}
}template <typename T> 
inline T CeilDiv(const T& a, const T& b) {return (a + b - 1) / b;
}int main(){size_t block_size = 128;size_t N = 1 * 1024;size_t bytes_A = sizeof(float) * N;size_t bytes_B = sizeof(float) * N;float* h_A = (float*)malloc(bytes_A);float* h_B = (float*)malloc(bytes_B);for( int i = 0; i < N; i++ ){h_A[i] = (i / 666) * ((i % 2 == 0) ? 1: -1);}float* d_A;float* d_B;checkCudaErrors(cudaMalloc(&d_A, bytes_A));checkCudaErrors(cudaMalloc(&d_B, bytes_B));checkCudaErrors(cudaMemcpy( d_A, h_A, bytes_A, cudaMemcpyHostToDevice));cudaEvent_t start, stop;checkCudaErrors(cudaEventCreate(&start));checkCudaErrors(cudaEventCreate(&stop));float msec = 0;int iteration = 1000;checkCudaErrors(cudaEventRecord(start));for(int i = 0; i < iteration; i++){//sigmoid<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, N);//sigmoid_float4<<<CeilDiv(N, block_size), block_size/4>>>(d_A, d_B, N);sigmoid_float4<<<CeilDiv(N/4,block_size), block_size>>>(d_A, d_B, N);}checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&msec, start, stop));printf("sigmoid takes %.3f msec\n", msec/iteration);checkCudaErrors(cudaMemcpy(h_B, d_B, bytes_B, cudaMemcpyDeviceToHost));for(int i = 0; i < N; i++){double err = fabs(h_B[i] - 1.0f/(1.0f + expf(-h_A[i])));if(err > 1.e-6) {printf("wrong answer!\n");break;}}cudaFree(d_A);cudaFree(d_B);free(h_A);free(h_B);return 0;
}

编译和运行代码:

nvcc -o sigmoid sigmoid.cu
./sigmoid 

4.2、relu.cu

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <vector>
#include<assert.h>
#include <algorithm>
#include <cublas_v2.h>
#include <cuda_runtime.h>#define FLOAT4(value)  *(float4*)(&(value))#define checkCudaErrors(func)               \
{                                   \cudaError_t e = (func);         \if(e != cudaSuccess)                                        \printf ("%s %d CUDA: %s\n", __FILE__,  __LINE__, cudaGetErrorString(e));        \
}//nvcc -o relu relu.cu && ./relu
//sigmoid<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, N)
//a: Nx1, b: Nx1, c: Nx1, y = relu(x)
__global__ void relu(float* x, float* y, int N){int idx = blockIdx.x * blockDim.x + threadIdx.x;if(idx < N) y[idx] = fmaxf(0.0f, x[idx]);
}__global__ void relu_float4(float* x, float* y, int N){int idx = (blockIdx.x * blockDim.x + threadIdx.x) * 4;if(idx < N){float4 tmp_x = FLOAT4(x[idx]);float4 tmp_y;tmp_y.x = fmaxf(0.0f, tmp_x.x);tmp_y.y = fmaxf(0.0f, tmp_x.y);tmp_y.z = fmaxf(0.0f, tmp_x.z);tmp_y.w = fmaxf(0.0f, tmp_x.w);FLOAT4(y[idx]) = tmp_y;}
}template <typename T> 
inline T CeilDiv(const T& a, const T& b) {return (a + b - 1) / b;
}int main(){size_t block_size = 128;size_t N = 1 * 1024;size_t bytes_A = sizeof(float) * N;size_t bytes_B = sizeof(float) * N;float* h_A = (float*)malloc(bytes_A);float* h_B = (float*)malloc(bytes_B);for( int i = 0; i < N; i++ ){h_A[i] = (i / 666) * ((i % 2 == 0) ? 1: -1);}float* d_A;float* d_B;checkCudaErrors(cudaMalloc(&d_A, bytes_A));checkCudaErrors(cudaMalloc(&d_B, bytes_B));checkCudaErrors(cudaMemcpy( d_A, h_A, bytes_A, cudaMemcpyHostToDevice));cudaEvent_t start, stop;checkCudaErrors(cudaEventCreate(&start));checkCudaErrors(cudaEventCreate(&stop));float msec = 0;int iteration = 1000;checkCudaErrors(cudaEventRecord(start));for(int i = 0; i < iteration; i++){//relu<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, N);//relu_float4<<<CeilDiv(N, block_size), block_size/4>>>(d_A, d_B, N);relu_float4<<<CeilDiv(N/4, block_size), block_size>>>(d_A, d_B, N);}checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&msec, start, stop));printf("relu takes %.3f msec\n", msec/iteration);checkCudaErrors(cudaMemcpy(h_B, d_B, bytes_B, cudaMemcpyDeviceToHost));for(int i = 0; i < N; i++){double err = fabs(h_B[i] - fmaxf(0.0f, h_A[i]));if(err > 1.e-6) {printf("wrong answer!\n");break;}}cudaFree(d_A);cudaFree(d_B);free(h_A);free(h_B);return 0;
}

编译和运行代码:

nvcc -o relu relu.cu
./relu

4.3、histogram.cu

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <vector>
#include<assert.h>
#include <algorithm>
#include <cublas_v2.h>
#include <cuda_runtime.h>#define INT4(value) *(int4*)(&(value))
#define FLOAT4(value)  *(float4*)(&(value))#define checkCudaErrors(func)               \
{                                   \cudaError_t e = (func);         \if(e != cudaSuccess)                                        \printf ("%s %d CUDA: %s\n", __FILE__,  __LINE__, cudaGetErrorString(e));        \
}/*
x[i] = i-- -- - -- - - -
- - - - -
考虑一个warp里相邻线程对全局内存y的访问是否合并(coalesced global access)
warp thread[0]: 0, 1, 2, 3, 
warp thread[1]: 4, 5, 6, 7
*/
__global__ void histogram(int* x, int* y, int N){int idx = blockIdx.x * blockDim.x + threadIdx.x;if(idx < N) atomicAdd(&y[x[idx]], 1);
}__global__ void histogram_int4(int* x, int* y, int N) {int idx = 4 * (blockIdx.x * blockDim.x + threadIdx.x);if (idx < N) {int4 tmp_y = INT4(x[idx]);atomicAdd(&(y[tmp_y.x]), 1);atomicAdd(&(y[tmp_y.y]), 1);atomicAdd(&(y[tmp_y.z]), 1);atomicAdd(&(y[tmp_y.w]), 1);}
}template <typename T> 
inline T CeilDiv(const T& a, const T& b) {return (a + b - 1) / b;
}int main(){size_t block_size = 128;size_t N = 32 * 1024 * 1024;size_t bytes_A = sizeof(int) * N;size_t bytes_B = sizeof(int) * N;int* h_A = (int*)malloc(bytes_A);int* h_B = (int*)malloc(bytes_B);for( int i = 0; i < N; i++ ){h_A[i] = i;}int* d_A;int* d_B;checkCudaErrors(cudaMalloc(&d_A, bytes_A));checkCudaErrors(cudaMalloc(&d_B, bytes_B));checkCudaErrors(cudaMemcpy( d_A, h_A, bytes_A, cudaMemcpyHostToDevice));cudaEvent_t start, stop;checkCudaErrors(cudaEventCreate(&start));checkCudaErrors(cudaEventCreate(&stop));float msec = 0;int iteration = 1;checkCudaErrors(cudaEventRecord(start));for(int i = 0; i < iteration; i++){//histogram<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, N);//histogram_int4<<<CeilDiv(N, block_size), block_size/4>>>(d_A, d_B, N);histogram_int4<<<CeilDiv(N/4, block_size), block_size>>>(d_A, d_B, N);}checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&msec, start, stop));printf("histogram takes %.3f msec\n", msec/iteration);checkCudaErrors(cudaMemcpy(h_B, d_B, bytes_B, cudaMemcpyDeviceToHost));for(int i = 0; i < N; i++){//all ones;double err = fabs(h_B[i] - iteration * 1.0f);if(err > 1.e-6) {printf("wrong answer!\n");break;}}cudaFree(d_A);cudaFree(d_B);free(h_A);free(h_B);return 0;
}

编译和运行代码:

nvcc -o histogram histogram.cu
./histogram

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

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

相关文章

商家入驻平台怎么让资金自动分配给商家

最近很多上线了多商户电商系统的朋友咨询&#xff0c;我们平台的用户支付后&#xff0c;钱进入了我们的对公账户&#xff0c;怎样让钱在走完流程后&#xff0c;自动进入商家的账户呢&#xff1f;今天商淘云为您分享商户入驻平台自动分配给商家资金的三种方法。 首先是平台应建立…

Docker基础(一)

文章目录 1. 基础概念2. 安装docker3. docker常用命令3.1 帮助命令3.2 镜像命令3.3 容器命令3.4 其他命令 4. 使用案例 1. 基础概念 镜像&#xff08;Image&#xff09;&#xff1a;Docker 镜像&#xff08;Image&#xff09;&#xff0c;就相当于是一个 root 文件系统。比如官…

node14下运行项目报错:regeneratorRuntime is not defined

regeneratorRuntime is not defined&#xff0c;这是由于配置babel出错问题&#xff0c;由于使用了es7语法如async/await而当前babel版本过低 解决&#xff1a; npm install -D babel-plugin-transform-runtime babel-runtime 安装完成后在.babelrc文件下配置&#xff1a; &qu…

vue2与vue3的diff算法有什么区别

在 Vue 中&#xff0c;虚拟 DOM 是一种重要的概念&#xff0c;它通过将真实的 DOM 操作转化为对虚拟 DOM 的操作&#xff0c;从而提高应用的性能。Vue 框架在虚拟 DOM 的更新过程中采用了 Diff 算法&#xff0c;用于比较新旧虚拟节点树&#xff0c;找出需要更新的部分&#xff…

远程连接Redis

以连接阿里云上的Redis为例 1. 在阿里云安全组中开放端口 2.修改Redis启动时所用的配置文件&#xff08;redis.conf&#xff09; 2.1 修改ip地址 如图&#xff1a;将默认的本地ip bind 127.0.0.1地址改为bind 0.0.0.0 2.2 将保护模式关闭 将默认的 supervised yes 改为 n…

Transformer视频理解学习的笔记

今天复习了Transformer,ViT, 学了SwinTransformer, 还有观看了B站视频理解沐神系列串讲视频上&#xff08;24.2.26未看完,明天接着看&#xff09; 这里面更多论文见&#xff1a;https://github.com/mli/paper-reading/ B站视频理解沐神系列串讲视频下&#xff08;明天接着看&a…

QPair和QMap

QPair和QMap是Qt框架中用于不同目的的两种容器类型&#xff0c;它们各自有不同的使用场景和特点。 QPair QPair是一个模板类&#xff0c;用于存储一对值&#xff0c;这对值可以是任意类型。它类似于C标准库中的std::pair。QPair通常用于当函数需要返回两个值时或者需要将两个…

认证模式~

认证方式 基于Cookie和Session的认证方式 基于Cookie和Session的认证是传统的Web应用认证机制。它依赖于HTTP协议无状态的特性&#xff0c;在客户端&#xff08;浏览器&#xff09;和服务器之间保持用户的状态。 工作原理 用户登录&#xff1a;用户通过输入用户名和密码来登…

速盾网络:组建CDN服务器加速

在当前互联网时代&#xff0c;网站的速度对用户体验和搜索引擎排名至关重要。为了提升网站的访问速度和性能&#xff0c;越来越多的网站选择使用CDN&#xff08;内容分发网络&#xff09;服务。本文将介绍如何利用速盾网络来组建CDN服务器&#xff0c;以加速网站访问。 1. 选择…

Spring Boot:全面解析现代微服务构建利器

Spring Boot 是基于 Java 平台的开源框架&#xff0c;它通过提供一系列标准化的快速开发和部署功能&#xff0c;彻底革新了传统 Spring 应用程序的创建与管理方式。自2014年发布以来&#xff0c;Spring Boot 已经成为构建企业级、云原生以及微服务架构应用的首选工具之一&#…

嵌入式C语言(三)

typeof() 使用typeof可以获取一个变量或表达式的类型。 typeof的参数有两种形式&#xff1a;表达式或类型。 int i;typeof(i) j 20; --> int j 20;typeof(int *) a; -->int *a; int f(); -->typeof(f()) k;--? int k我们可以看出通过typeof获取一个变量的…

合并spark structured streaming处理流式数据产生的小文件

备注&#xff1a; By 远方时光原创&#xff0c;可转载&#xff0c;不能复制到其他平台 背景&#xff1a;做流批一体&#xff0c;湖仓一体的大数据架构&#xff0c;常见的做法就是 数据源->spark Streaming->ODS&#xff08;数据湖&#xff09;->spark streaming->…

Vue 实现页面导出A4标准大小的PDF文件,以及处理图片跨域不能正常展示的问题等

效果预览&#xff1a; 代码流程&#xff1a;首先在utils文件夹下创建htmlToPdf的js工具文件&#xff0c;然后在main.js中注册引用 htmlToPdf.js // 导出页面为PDF格式 import html2Canvas from html2canvas import JsPDF from jspdfexport default {install(Vue, options) {V…

hcia datacom课程学习(1):通信基础

1.总体框架 上图为发送方通过互联网传递信息给接收方的过程。 家用路由器会直接集成上图中的四层&#xff08;vlan&#xff0c;DHCP&#xff0c;静态路由&#xff0c;NAT&#xff0c;PPPoE&#xff09;。 2.网络性能指标 &#xff08;1&#xff09;带宽 单位时间内传输的数…

Linux 使用 SSH 传输文件

# 登录 ssh usernameip_address # 复制文件(远程-->本地) scp usernameip_address:/home/username/filename . # 复制文件(本地-->远程) scp filename usernameip_address:/home/username # 复制目录(本地-->远程) scp -r source_dir usernameip_address:/home/use…

独孤思维:这份付出,可以拿一辈子收益

学习&#xff0c;不能贪杯。 做副业&#xff0c;不能什么都做。 什么都学&#xff0c;就什么都学不会。 什么项目都做&#xff0c;就什么都赚不到钱。 这是一定的。 人的精力有限&#xff0c;时间有限&#xff0c;成本有限。 这一辈子&#xff0c;做好一件事就够了。 很…

解析Hadoop三大核心组件:HDFS、MapReduce和YARN

目录 HadoopHadoop的优势 Hadoop的组成HDFS架构设计Yarn架构设计MapReduce架构设计 总结 在大数据时代&#xff0c;Hadoop作为一种开源的分布式计算框架&#xff0c;已经成为处理大规模数据的首选工具。它采用了分布式存储和计算的方式&#xff0c;能够高效地处理海量数据。Had…

Redisson - 实现Java的Redis分布式和可扩展解决方案

Redisson - 实现Java的Redis分布式和可扩展解决方案 引言&#xff1a; 在现代的分布式系统中&#xff0c;缓存和数据存储扮演着至关重要的角色。Redis作为一种高性能的键值存储数据库&#xff0c;被广泛用于缓存、消息队列、实时数据分析等场景。然而&#xff0c;原生的Redis…

pod调度策略 标签管理 资源配额与限额 全局资源配额与限额策略,

打分也是基于可调度节点进行打分资源情况. 指定多个节点,会进行覆盖其之前节点名称 --- kind: Pod apiVersion: v1 metadata:name: myhttp spec:nodeName: node-0001 # 基于节点名称进行调度containers:- name: apacheimage: myos:httpd 基于节点名称的调度策略 标签与调…

数据可视化--了解数据可视化和Excel数据可视化

目录 1.1科学可视化&#xff1a; 可视化是模式、关系、异常 1.2三基色原理&#xff1a; 三基色:红色、绿色和蓝色 1.3Excel数据可视化 1.3.1 excel数据分析-13个图表可视化技巧 1.3.2 excel数据分析-28个常用可视化图表&#xff08;video&#xff09; 1.3.3Excel可视化…