共享内存概述

游戏数据存储设计概述(共享内存) 游戏数据存储设计概述(共享内存)                                                            2012-10-03   这里只说一下使用共享内存的游戏数据存储方式及设计,比较泛泛地作个介绍。           所谓共享内存存储,是指在内存中开辟一块共享内存,游戏服务器及数据存储程序都能对其进行读写操作,游戏服务器定时把数据写入到 阅读详情

目录

从本章往后将要讲解

共享内存概述

共享内存的生命周期

共享内存的读取分配

共享内存存储体

共享内存访问模式

内存填充

同步

弱排序内存模型

显式屏障/障碍

内存栅栏

Volatile修饰符

总结


从本章往后将要讲解

  • 数据在共享内存中的安排
  • 二维共享内存到线性全局内存的索引转换
  • 解决不同访问模式中的存储体中的冲突
  • 在共享内存中缓存数据以减少对全局内存的访问
  • 使用共享内存避免非合并全局内存的访问
  • 常量缓存和只读缓存之间的差异
  • 线程束洗牌指令编程

明白一个核心就知道为什么共享内存如此的重要

矩阵转置,无论是以行读还是列读,也就是朴素转置,我们都无法避免一个问题,绝对有一边是无法对齐合并的,那针对这种场景,是开发当中占绝大多数的,所以共享内存就是来解决这个场景的,如果都合并,你不使用共享内存也是可以达到很大的带宽,借助l1缓存l2缓存

  1. 随机索引查表 / 稀疏访问
  2. 图像邻域计算 / 模板卷积(这个的话就是每一行连续,但是上下相隔很远)
  3. 结构体数组(AoS)单字段访问
  4. 多维数组非首维度遍历

核心点就是无法按照内存存储的顺序进行访问

  • 大量真实算法的访问逻辑,天然就要求跨步、离散地访问全局内存,这种非合并不是代码写得不好,而是算法本身的数学逻辑决定的,无法从根源上消除;
  • 这种场景下,共享内存就是性能优化的核心手段 —— 把「全局内存的非合并低效访问」,转化为「合并读写全局内存 + 共享内存内自由访问」,用极低的共享内存开销,换取全局内存带宽的大幅提升。

共享内存概述

nvidia-ada-gpu-architecture.pdf

┌───────────────────────────────────────────────────────┐
│                      SM (Ada)                         │
├───────────────────────────────────────────────────────┤
│  [寄存器文件] 256 KB (独立)                            │
│                                                       │
│  [L1 / 共享内存] 128 KB (共享,可配置划分)              │
│  │  └── (内部划分:共享内存 + L1 数据缓存)              │
│  │  └── **L1 数据缓存同时承担以下角色:**               │
│  │      1. 普通 L1 缓存 (全局内存、局部内存的读写)      │
│  │      2. 纹理缓存 (纹理请求的缓存,逻辑通路)          │
│  │      3. 只读缓存 (通过 __ldg() 触发的只读缓存通路)   │
│         4. 常量缓存                                   │
│                              │
│  [指令缓存] 32 KB (独立 SRAM)                          │
└───────────────────────────────────────────────────────┘

这个是ai根据白皮书总结出来的,但问了多个ai都有出入,所以具体的大家可以看白皮书

因为白皮书当中没有提到常量纹理和只读,单单提到了一个指令缓存,所以其余的缓存可能已经并入l1缓存了,具体的大家可以先看一下白皮书,因为博主问了多个ai总结都有出入

但是不妨碍接下来的讲解,后续如果博主找到相关资料或者能够验证再说

共享内存的作用

  • 线程块内高速协同通信,支撑并行算法
  • 转化全局内存非合并访问,根治带宽浪费
  • 缓存复用数据,大幅减少全局内存访问次数
  • 缓解寄存器压力,替代寄存器溢出

本章我们研究两个例子:

  1. 归约核函数
  2. 矩阵转置核函数

共享内存的生命周期

从全局内存过来的全都要经过L2缓存,相比之下l1缓存和共享内存更接近cuda核心,读取效率高于l2,带宽高,延迟低

共享内存的生命周期:随着 Block 分配和释放

共享内存是 Block 独占的,它的生命周期和 Block 绑定。当一个 Block 被全局调度器(GigaThread Engine)分配到某个 SM 上时,SM 的硬件资源管理器就会把 Block 请求的共享内存大小,从 SM 的共享内存池里一次性划拨出来给这个 Block。直到 Block 的所有线程都执行完毕、退出 SM,这块共享内存才会被释放回池子里。

同一个 SM 上可以同时驻留多个 Block,每个 Block 都有自己独立的一份共享内存,彼此互不可见。如果一个 Block 用了 32KB 共享内存,SM 总共有约 100KB,那么这个 SM 最多能同时容纳 3 个这样的 Block。如果 Block 只用了 16KB,SM 就能同时容纳 6 个。这就是共享内存对占用率的限制——每个 Block 用的共享内存越多,SM 能同时容纳的 Block 越少。

共享内存block内共享,block之间是互相独立的,如果单个block的占用共享内存越大,整体的占有率就越少

共享内存的读取分配

1. 静态声明(编译时决定大小)

这是最简单、最常用的方式。在核函数内部,用 __shared__ 关键字直接声明一个固定大小的数组。大小必须是一个编译期常量,不能是变量。

__global__ void kernel_static() {
    __shared__ float tile[32][32];  // 大小固定为 32×32×4 = 4096 字节
    // ...
}

特点:

  • 简单直接,大小在编译时就完全确定。

  • 受限于每个 Block 最大共享内存上限(通过 cudaDeviceProp.sharedMemPerBlock 查询,通常约 48 KB 或 100 KB)。

  • 如果超出硬件限制,编译器会直接报错。


2. 动态声明(运行时决定大小)

当共享内存的大小依赖输入数据或运行环境时,就需要动态声明。它分两步完成:

第一步:在核函数内用 extern __shared__ 声明

__global__ void kernel_dynamic(int *input, int *output, int N) {
    extern __shared__ float cache[];  // 大小由主机端启动时指定
    // ...
}

关键限制:一个核函数只能声明一个动态共享内存数组。如果需要多个数组,必须手动用指针偏移来划分。

第二步:在主机端启动核函数时,通过 <<< >>> 的第三个参数指定大小

int sharedMemSize = block.x * sizeof(float);  // 例如每个线程一个 float
kernel_dynamic<<<grid, block, sharedMemSize>>>(d_input, d_output, N);

<<<grid, block, sharedMemSize>>> 中的第三个参数就是动态共享内存的总字节数。

如果动态声明需要多个数组:

必须手动用指针偏移来划分:

__global__ void kernel_dynamic(int *input, int *output, int N) {
    extern __shared__ float raw_memory[];

    // 手动划分:前 N 个 float 给 A,后 N 个给 B
    float *A = raw_memory;
    float *B = &raw_memory[N];

    // 现在可以像使用独立数组一样使用 A 和 B
    // ...
}

注意:必须确保主机端传入的 sharedMemSize 等于你手动划分的所有数组的总字节数。

动态共享内存的典型应用场景:

  1. 处理变长数据:输入数据量不确定,需要根据实际大小分配共享内存。

  2. 提高通用性:同一个核函数可以处理不同规模的 tile,而不必为每个 tile 大小写一个版本。

  3. 节省编译时间:避免为不同大小生成多个核函数实例。

  4. 设备限制未知:在运行时根据 GPU 的实际共享内存上限(cudaDeviceProp.sharedMemPerBlock)动态决定大小。


3. 调整 L1 缓存与共享内存的硬件划分

共享内存和 L1 缓存共用同一块片上 SRAM,你可以通过 API 改变两者的大小比例,以获得更多共享内存容量。这不是分配共享内存本身,而是扩大共享内存的总容量上限。

方式一:针对单个核函数设置

cudaFuncSetCacheConfig(my_kernel, cudaFuncCachePreferShared);
// 在此之后启动 my_kernel,就会采用优先共享内存的划分方案
my_kernel<<<grid, block>>>(...);

可选配置:

配置选项

含义

cudaFuncCachePreferNone

默认配置,不主动偏好

cudaFuncCachePreferShared

偏好共享内存,让共享内存尽可能大,L1 尽可能小

cudaFuncCachePreferL1

偏好 L1 缓存,让 L1 尽可能大,共享内存尽可能小

cudaFuncCachePreferEqual

均分,两者各占一半

方式二:针对整个设备设置

cudaDeviceSetCacheConfig(cudaFuncCachePreferShared); 
// 影响之后所有核函数
//如何查询调整后的实际容量?
//用 cudaDeviceGetAttribute 在运行时查询:
int sharedSize, l1Size;
cudaDeviceGetAttribute(&sharedSize, cudaDevAttrMaxSharedMemoryPerBlockOptin, 0);
cudaDeviceGetAttribute(&l1Size, cudaDevAttrMaxL1CacheSize, 0);
printf("最大可选共享内存: %d bytes\n", sharedSize); // 如 102400 (100KB)
printf("L1 缓存大小: %d bytes\n", l1Size);        // 如 28672 (28KB)
  • sharedSize 就是你设置为 PreferShared 后每个 Block 可用的最大共享内存(约 100KB)。

  • 这个值也等于 cudaDeviceProp.sharedMemPerBlockOptin。


总结:三种“分配”的区别与联系

层面

方法

决定时机

灵活性

关键语法/API

声明共享内存

静态声明

编译时

低,固定大小

__shared__ int arr[1024];

指定共享内存

动态声明

运行时

高,大小可变

extern __shared__ float arr[]; + <<<grid, block, bytes>>>

调整容量上限

L1/共享划分

运行时

控制总量

cudaFuncSetCacheConfig / cudaDeviceSetCacheConfig

根据你的需求选择合适的方式。如果需要共享内存大小依赖输入数据,就用动态声明;如果算法固定,静态声明更简洁。如果应用对共享内存需求极大,再考虑调整划分比例。

共享内存存储体

了解完分配要了解其特性,对于全局内存的带宽和延迟,如何采用共享内存去优化,学会优化的前提是理解其物理特性,之前就简单的提到过,但是这里将详细的从头到尾介绍

共享内存物理上被分为 32 个宽度为 4 字节的 Bank。

也就是你可以看成32个通道,为什么是32?这个和warp是对应的32线程

如果你的线程访问共享内存,如果32线程都访问不同的bank,那就不会冲突,一个事务就可以,如果多个线程访问同一个 Bank 的不同地址,就发生 Bank Conflict,请求被串行化,导致多个事务才能拿到数据,这样带宽利用率低

注意:32个bank通道是SM级别的,也就是所有的warp共享32个,只有32个

L1缓存不是按照bank的设计,而是按照我们之前所讲的sector+cacheLine设计的

为什么不是 Bank 结构? 因为 L1 缓存服务的请求来源更复杂——不同的 Warp、不同的地址、不同的时间。它的目标是提高命中率、减少延迟,而不是支持 32 个线程同时无冲突地访问不同地址。用 Bank 结构反而会增加硬件复杂度和功耗。

那 L1 缓存内部怎么处理并行请求? L1 缓存控制器内部有多条独立的流水线通道,可以同时处理来自不同 LSU 的请求。这和共享内存的 Bank 不是同一个概念——Bank 是存储本身的并行访问能力,而 L1 的流水线并行是请求处理能力。

设想一下场景:对于之前的转置,如果你的共享内存还设计成L1缓存那样,那合并不了,带宽就很小,为什么采用bank?核心就是因为我压根不需要合并,我只要我的数据落在不同的bank,我就可以一个周期访问数据,那带宽就得到极大的提升,这就是为什么要设计出来bank。如果没有 Bank 结构,共享内存内部同一时间只能处理一个请求。那对于访问,这 32 个线程的请求就会全部串行,共享内存的优势就荡然无存了。Bank 结构,就是为了在“无法合并”的情况下,仍然提供最大并行能力。

简单说,Bank 和合并访问是相反的策略。合并访问是“把多个请求捏成一个”,Bank 是“提供多个通道同时处理多个请求”。


注意:

线程束访问共享内存的时候有下面3种经典模式:

  1. 并行访问,多地址访问多存储体
  2. 串行访问,多地址访问同一存储体
  3. 广播访问,单一地址读取单一存储体(访问同一个地址是可以广播的,这不会冲突)

冲突是指:一个bank里面是有多个不同的地址的,如果你访问的是一个bank里面的不同地址就会冲突,如果是一个bank里面相同的地址就可以广播

以下说的就是访问同一个bank里面的不同地址导致的bank conflict

流程:

  1. Warp Scheduler 发射 LDS(Load Shared)或 STS(Store Shared)指令给 LSU。

  2. LSU 收集 32 个线程的地址,但不进行合并(因为共享内存不走 L1/L2 缓存,是直接访问 SRAM)。

  3. LSU 通过交叉开关(Crossbar)直接访问共享内存的 32 个 Bank。

  4. 如果发生 Bank Conflict,请求被串行化;如果没有冲突,32 个请求在一个时钟周期内同时完成。

  5. LSU 把读到的数据写入各线程的寄存器(Load),或把寄存器数据写入共享内存(Store)。

  • Warp Scheduler:厨师长,负责指挥:“配菜员,去仓库拿 32 份胡萝卜!”

  • LSU:专职配菜员,负责执行命令:跑去仓库(缓存/DRAM),把胡萝卜拿回来,分给 32 个厨师(CUDA 核心)。

  • 共享内存:厨房里的公共调料架,配菜员可以直接从架子上取调料(直接访问 SRAM),不需要跑仓库。

Warp调度器0     Warp调度器1     Warp调度器2     Warp调度器3
     │              │              │              │
     ▼              ▼              ▼              ▼
  warp0的         warp1的         warp2的         warp3的
  smem指令        smem指令        smem指令        smem指令
     │              │              │              │
     └──────────────┴──────────────┴──────────────┘
                          │
                          ▼
                    LSU指令队列
                    (串行出队)
                          │
                  ┌───────▼────────┐
                  │    仲裁器       │
                  │ 分析32个地址    │
                  │ 落在哪些bank    │
                  └───────┬────────┘
                          │
              ┌───────────┴───────────┐
              ▼                       ▼
       无冲突或广播              有n路冲突
       1个周期完成              n个周期完成
       bank conflict+0         bank conflict+(n-1)
              │                       │
              └───────────┬───────────┘
                          ▼
                    32个Bank的SRAM
                    (实际读写操作)
理想情况(无冲突):
LSU0的warp请求bank0,1,2...7
LSU1的warp请求bank8,9,10...15
LSU2的warp请求bank16,17...23
LSU3的warp请求bank24,25...31

→ 四组请求访问不同bank
→ Crossbar同时建立四条独立连接
→ 一个周期内完成所有四个warp的访问
→ 四个LSU真正并行工作

仲裁器位于Crossbar的入口
每个时钟周期做两件事:

第一件事:处理同一LSU内部的bank conflict
    LSU0发来32个线程的地址
    仲裁器分析这32个地址落在哪些bank
    如果有冲突 → 拆分成多个轮次串行处理
    如果无冲突 → 一个周期完成

第二件事:处理不同LSU之间的bank竞争
    LSU0的warp要访问bank5
    LSU1的warp也要访问bank5
    → 同一个bank同一时刻只能服务一个请求
    → 仲裁器决定谁先访问
    → 另一个等待下一个周期

其实本质要管好一个warp的32线程即可,让这32个线程在访问共享内存的时候尽量在不同的bank,或者说如果冲突只要少量也可以,不要全部都冲突,那效率极差

冲突的根本原因:访问同一个bank的不同地址

所以我们要调整数据、代码、算法去规避冲突,提升性能

共享内存访问模式

架构Bank宽度说明
Fermi (2.x)4字节老架构
Kepler (3.x)8字节历史数据,已过时
Maxwell及以后4字节现代架构统一标准
#include <cuda_runtime.h>
#include "../freshman.hpp"
using namespace std;
int main()
{
    int device = 0;
    cudaSetDevice(device);
    cudaSharedMemConfig config;
    CHECK(cudaDeviceGetSharedMemConfig(&config));
    cout << "SharedMem Config value: " << config << endl;
    if (config == cudaSharedMemBankSizeFourByte){
        cout << "Mode: 4 Byte per Bank" << endl;
    }
    else if (config == cudaSharedMemBankSizeEightByte){
        cout << "Mode: 8 Byte per Bank" << endl;
    }
    else{
        cout << "cudaSharedMemBankSizeDefault" << endl;
    }
    cudaSharedMemConfig rconfig=cudaSharedMemBankSizeEightByte;
    cudaDeviceSetSharedMemConfig(rconfig);
    CHECK(cudaDeviceGetSharedMemConfig(&config));
    cout << "SharedMem Config value: " << config << endl;
    if (config == cudaSharedMemBankSizeFourByte){
        cout << "Mode: 4 Byte per Bank" << endl;
    }
    else if (config == cudaSharedMemBankSizeEightByte){
        cout << "Mode: 8 Byte per Bank" << endl;
    }
    else{
        cout << "cudaSharedMemBankSizeDefault" << endl;
    }
    return 0;
}

bank大小仍然为四字节,说明已经被锁定了

存储索引按照以下方式进行计算

Bank_Index = (Byte_Address / 4) % 32

内存填充

padding填充:https://mp.youkuaiyun.com/mp_blog/creation/editor/162274281

在这篇文章里面我已经讲的很详细,并且还有实验

简单来说就是多开一列,然后让数据映射关系不是整齐的,也就是同一列不是存储在同一个bank当中,当然本身一行也不是,为的就是访问一列的时候让其不产生bank conflict

同步

一个block被调度到SM上,然后又被划分为warp被warp schedule调度,一个warp通常是32个线程,就算不满也是按照32个来执行

那么就注定了一个block内的线程是不同步的,但是一个warp内的32线程是同步的(注意线程束分化这种情况,也就是if-else,这种会破坏天然的同步性,特性情况)

方法:

  1. 屏障同步(Barrier / 障碍):既阻塞执行、又保证内存可见性
  2. 内存栅栏(Memory Fence):只保证内存操作顺序可见性,不阻塞线程执行

弱排序内存模型

什么叫弱排序,就是内存访问不一定严格按照代码书写顺序执行和可见。

对于多线程并发的情况,如果线程A先写入x,再写y,但是在线程B看到的是y,此次才是x,这种情况就是弱排序,不保证顺序

1. 写操作:不是乱序执行,而是编译器可能缓存数据

对于同一个线程内,有数据依赖的内存访问,硬件和编译器保证顺序。对于没有依赖的访问,编译器可能把数据临时缓存在寄存器里,直到“合适的时候”才真正写入内存。

最常见的情况是编译器优化:你写了两行赋值,先写 a[0] 再写 b[0]。编译器发现这两者没有依赖,可能把 b[0] 的结果先写回显存,而把 a[0] 的值暂留在寄存器里稍后再写。不是硬件写操作乱序了,而是编译器改变了实际物理写入的时间点。

2. 读操作:指令级并行可能导致重排

假设你写了两条读指令,分别读 a[0] 和 b[0]。这两者没有依赖,硬件可以同时发射这两条指令,或者后发射的先返回数据。这是为了填满流水线、提高吞吐量,是性能优化,不是Bug。 这和CPU的乱序执行是一个道理。

3. 跨线程可见性:核心在于同步点,而不是执行顺序

在没有同步点的情况下,线程 A 对全局内存的写入,什么时候、以什么顺序被线程 B 看到,是无法保证的。因为数据可能还在 A 的 L1 缓存里,还没写回 L2 或显存。__syncthreads 不仅是一个障碍同步,它还包含一个内存栅栏,强制所有之前的写入对 Block 内其他线程可见。这就是为什么在共享内存的写入和读出之间必须加 __syncthreads——没有它,即使你的代码写入了数据,其他线程也读不到。

为了性能,这是根本原因:

  1. 如果要保证强一致,每次写入都得立刻刷新到所有缓存,带宽开销极大;
  2. 弱排序让硬件可以自由地批量刷新缓存、乱序执行,最大化吞吐量;
  3. GPU 的设计哲学是 "吞吐量优先,一致性按需保证"—— 平时放开跑,需要顺序的时候用同步指令自己建。

为了可控,必须使用同步技术

显式屏障/障碍

CUDA中,障碍点设置在核函数中,注意这个指令只能在核函数中调用,并只对同一线程块内线程有效。

void __syncthreads();
  1. __syncthreads()作为一个障碍点,他保证在同一线程块内所有线程没到达此障碍点时,不能继续向下执行。
  2. 同一线程块内此障碍点之前的所有全局内存,共享内存操作,对后面的线程都是可见的。
  3. 这个也就能解决同一线程块内,内存竞争的问题,同步,保证先后顺序,不会混乱。
  4. 避免死锁情况出现,比如下面这种情况,就会导致内核死锁:

    if (threadID % 2 == 0) {
        __syncthreads();
    } else {
        __syncthreads();
    }
    //因为偶数线程的__syncthreads()导致一直等待奇数线程,连if都出不去,死锁了
  5. 只能解决一个块内的线程同步,想做块之间的,只能通过核函数的执行和结束来进行块之间的同步。(把要同步的地方作为核函数的结束,来隐式的同步线程块)

内存栅栏

内存栅栏能保证栅栏前的内核内存写操作对栅栏后的其他线程都是可见的,有以下三种栅栏:块,设备,系统。

线程块内:保证同一block中的其他线程对于栅栏前的内存写操作可见

void __threadfence_block();

设备:保证整个SM的所有线程对于栅栏前的内存写操作可见

void __threadfence();

系统:保证整个系统可见

void __threadfence_system();

栅栏的作用就是保证栅栏之前的所有写入内存的操作不会停留在寄存器,会刷新到对应的地方让其他可见,解决的是可见性问题

假设这个是warp0

data[0] = 1;      // 写入 A
__threadfence();  // 栅栏:强制 A 在 B 之前完成
ready = 1;        // 写入 B

这个是warp1

if (ready == 1) {
    use(data[0]);  // 希望读到 Warp 0 写入的 1
}

那么栅栏的作用是啥?

是warp能够读取的时候,data肯定写到了全局内存当中,一定能读到新值,所以栅栏的作用就是保证你在ready读取的时候,data一定是新值,就是类似一个栅栏,不允许重排

所以,栅栏的价值在于:它消除了“看到 ready=1 却读不到 data[0]=1”这种不一致的情况。

要保证所有的线程同一时间看到最新值,这个是__syncthreads()去做的

  • 栅栏:只保证“我写入的东西,在某个时间点之后对别人可见”,但不能保证“别人什么时候去看”。

  • 障碍:强制所有线程在同一个点集合,确保所有人都完成了写入,然后所有人再一起去看。

Volatile修饰符

volatile声明一个变量,防止编译器优化,防止这个变量存入缓存,如果恰好此时被其他线程改写,那就会造成内存缓存不一致的错误,所以volatile声明的变量始终在全局内存中。

总结

本文仅仅是讲解了理论知识,接下来就是围绕上述来展开

CUDA C编程(十一)CUDA共享内存概述   GPU有两种类型的内存:板载内存以及片上内存。其中全局内存是较大的板载内存,具有相对较高的延迟。共享内存是较小的片上内存,具有相对较低的延迟,并且共享内存可以提供比全局内存高得多的带宽。可以把它当作一个可编程管理的缓存,共享内存通常的用途有:块间线程通信的通道;用于全局内存数据的可编程管理的缓存;高速暂存存储器,用于转换数据以优化全局内存访问模式。 共 享 内 存    共 享 内 存 分 配 共 享 内 存 存 储 体 和 访 问 模 式 配 置 共 享 内 存 量 同 步 ... 阅读详情

相关推荐

第七篇:共享内存概述

好的,我们用一个清晰的、C语言的角度来解释共享内存的原理。

u013089100的博客 752

linux——共享内存概述

3、数据交换——写入(strcpy),读取(p指针指向内存空间,print(p))先运行./w文件写入共享空间内容,再打开一个界面./r来读取共享空间中的内容。6、信号量(不作为进程间通信的方式,控制一个临界资源)需要信号量来控制共享内存同时不能有两个进程写入(对应后边博客信号量)4、共享内存(有一块公共内存shm可挂载A\B上)3、消息队列(可以发送,也能接收消息)1、创建/打开共享内存——shmget。5、干掉共享内存——shmctl。4、释放共享内存——shmdt。2、映射——shmat。

2302_76515999的博客 231

Linux进程间通信(IPC)之共享内存的简单编程

1.共享内存的概述 共享内存(Shared Memory),是指两个或多个进程共享一个给定的存储区。在所有的IPC中,共享内存是用的比较多的IPC方式。 Linux系统中可以用 “ipcs -m” 指令查看共享内存,可以使用 “ipcrm -m 共享内存ID”来删除系统中的共享内存。 2.共享内存的特点 共享内存是最快的IPC方式,因为进程会对内存进行直接的访问。 共享内存可以允许多个进程同时操作,所以需要进行同步。 信号量和共享内存通常搭配使用,信号量的作用主要是用来同步对共享内存的访问。 3

ThePowerLin的博客 727

3.11共享内存概述(第二阶段)

3.11共享内存概述(第二阶段) 理论知识图 ** **

XFerrari的博客 121

方差分解分析变量筛选与显著性计算

冗余分析(redundancy analysis,RDA)是一种回归分析结合主成分分析的排序方法,也是多响应变量(multiresponse)回归分析的拓展。在进行VPA时,首先就要对这些环境因子进行一个分类,然后在约束其它类环境因子的情况下,对某一类环境因子进行排序分析,这种分析也成为偏分析,即partialRDA。在地球环境科学的实际应用中,可以基于VPA结果得出不同类型的环境因素(如何气候、土壤性质以及植物)对生物群落组成(如植被丰度,微生物群落)的解释程度。对于由或创建的排序对象,函数。

wlh2067的博客 2520

代付系统/代付系统源码/支付宝代付系统/API代付系统

简介: 其他站买的,没有做测试,源码是JAVA的,附带了安装文档,需要的拿无售后 源码下载地址https://www.skpan.cn/2QnIARSZDxG 代付系统/代付系统源码/支付宝代付系统/API代付系统 系统流程: 后台开通代理——开通商户——商户做代付(后台手工提交和 api 提交两种方式) 1、纯代付系统 2、支持个人支付宝全自动 3、支持单笔/批量/API接口方式代付 4、支持白名单、谷歌验证 5、收付款模式采用自动回调/自动转账 一、运营平台 商户管.

刀客源码网 5579

【Linux基础】共享内存概述

共享内存可以说是最有用的进程间通信方式。不同进程共享内存的意思是:同一块物理内存被映谢到进程各自的进程地址空间,不同进程可以及时看到某进程对共享内存的数据进行更新。采用内存共享通信的显而易见的好处是效率高,进程可直接读写内存,不需要任何数据的复制。当多个进程共享一段内存时,这时就需要某种同步机制了,如前而所说的互斥锁。 共享内存和进程间的关系图如图 9‑4所示:   01

u010926630的专栏 470

Simulink中扩张状态观测器ESO搭建与参数整定实战

扩张状态观测器(ESO)是自抗扰控制(ADRC)的核心,用于估计系统总扰动并实时补偿。其原理是将扰动扩张为状态变量,通过观测器带宽配置增益,实现扰动估计。在Simulink中搭建ESO可直观验证连续与离散域性能,解决参数整定与噪声折中问题。典型应用包括运动控制、电力电子和机器人驱动。本文基于MATLAB/Simulink,详细讲解线性ESO的模型结构、参数计算、离散化实现及常见问题排查,帮助读者从零搭建并调试ESO仿真。

weixin_34116482的博客 223

006_ipc概述及共享内存

共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。共享内存的使用可以提高多进程应用程序的性能,但同时也需要谨慎处理同步和错误处理,以确保数据的一致性和系统的稳定性。:用于标识共享内存段的唯一键值。:共享内存段具有权限设置,包括读、写和执行权限,以控制不同进程对共享内存段的访问。:共享内存的基本单位,是一个连续的内存区域,可以被多个进程映射到它们的地址空间。:共享内存段的大小可以根据需要设置,但通常受限于系统内存的大小。

qq_47325567的博客 1272

如何浏览与下载全球免费的地图高分辨率(亚米级)的遥感影像?

前言 如果你想使用商业卫星的高分辨遥感影像,一般价格昂贵。我们可以使用地图平台上免费的高分辨率数据。目前数据源很多,比如说google earth、天地图、四维地球、必应地图等等。这些影像统一有一个共同点就是:RGB三波段;Web地图瓦片服务。这意味着我们可以通过瓦片服务下载影像,但下载的影像只有三个波段,分别是 一.引言 二、可使用高分辨率的地图数据源 1.Google Earth影像 2.Esri World Image 3.天地图 4.必...

锐多宝的地理空间 2万+

AIOps探索 | 如何用大模型构建高效运维知识及智能问答平台(1)

好久没有分享AIOps相关干货了,最近大模型这么火,而且我们有客户已经基于大模型构建了知识及智能问答平台,需要在运维领域结合使用场景进行应用。为此,楼主从头到尾声梳理了一遍大模型在运维领域构建知识及智能问答平台的应用场景、技术实现,总结出来供大家一同探索,希望对大家理解和运用大模型有一定帮助~

智能运维及数据中台探索 2810

10.共享内存概述

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。 特点: (1)共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。 (2)因为多个进程可以同时操作,所以需要进行同步 (3)信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。 ...

m0_46889275的博客 223

AI上推荐 之 NeuralCF与PNN模型(改变特征交叉方式)

1. 前言 随着信息技术和互联网的发展, 我们已经步入了一个信息过载的时代,这个时代,无论是信息消费者还是信息生产者都遇到了很大的挑战: 信息消费者:如何从大量的信息中找到自己感兴趣的信息? 信息生产者:如何让自己生产的信息脱颖而出, 受到广大用户的关注? 为了解决这个矛盾, 推荐系统应时而生, 并飞速前进,在用户和信息之间架起了一道桥梁,一方面帮助用户发现对自己有价值的信息, 一方面让信息能够展现在对它感兴趣的用户前面。 推荐系统近几年有了深度学习的助推发展之势迅猛, 从前深度学习的传统推荐模型(协

Miracle8070 1万+

【CUDA 】第5章 共享内存和常量内存——5.1 CUDA共享内存概述(1)

板载内存全局(global)内存是最大的板载内存,延迟较高片上内存共享(shared)内存是较小的片上内存,延迟较低,带宽很高(可以看作可编程缓存)共享内存的用途:1、块内线程通信的通道2、全局(global)内存数据的可编程管理的缓存3、高速暂存存储器:转换数据来优化全局内存访问模式。

weixin_44231807的博客 1122

【HYSPLIT 教程第二期】Inline WRF-HYSPLIT 耦合

【HYSPLIT 教程第二期】Inline WRF-HYSPLIT 耦合

WW、forever的博客 810

SMMU软件指南SMMU编程之流表(Stream Table)

SMMU使用一组内存中的数据结构来定位翻译数据,详见“转换过程概述”(Translation process overview)。SMMU_(*_)STRTAB_BASE 存储初始结构(流表,Stream Table)的基地址。流表项(Stream Table Entry, STE)包含二级(Stage 2)转换表的基址指针,同时也定位一级(Stage 1)配置结构,这些配置结构包含转换表的基址指针。 传入事务的 StreamID,由 SEC_SID 进行限定,决定了用于查找的流表,并定位相应的STE。

AI-安全-功耗 1189

linux进程通信之共享内存的概述和使用

共享内存是两个进程共用一块公共内存,可以对起内存进行操作然后传送实现通信。 使用函数 1

weixin_48839038的博客 186

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【CUDA 】第5章 共享内存和常量内存——5.1 CUDA共享内存概述(2)

每个SM都有64KB片上内存共享内存和一级缓存共享该资源。CUDA两种配置方法(配置共享内存和一级缓存大小):①按设备配置②按核函数配置cudaFuncCachePreferNone: 无偏好,默认cudaFuncCachePreferShared:48KB共享内存+16KB一级缓存cudaFuncCachePreferL1: 16KB共享内存+48KB一级缓存cudaFuncCachePreferEqual: 32KB共享内存+32KB一级缓存。

weixin_44231807的博客 1242

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【CUDA 基础】5.1 CUDA共享内存概述

Abstract: 本文为CUDA内存的概述,介绍共享内存的模型,分配,访问,配置,同步等内容 Keywords: 模型,分配,访问,配置,同步 开篇废话 这里首先要进一步说明一下,前面我们在说缓存的时候说其是可编程的,这是不准确的,应该说是可以控制的,而我们今天要说的共享内存才是真正意义上的可编程的。 废话不多说了,一套CUDA内容写到现在,一大半已经进行完了,希望我们在一个系列...

Tony的博客 964

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

访问时发生了共享冲突是什么意思_【CUDA 基础】5.1 CUDA共享内存概述

Abstract: 本文为CUDA内存的概述,介绍共享内存的模型,分配,访问,配置,同步等内容Keywords: 模型,分配,访问,配置,同步开篇废话这里首先要进一步说明一下,前面我们在说缓存的时候说其是可编程的,这是不准确的,应该说是可以控制的,而我们今天要说的共享内存才是真正意义上的可编程的。废话不多说了,一套CUDA内容写到现在,一大半已经进行完了,希望我们在一个系列完成后都能有所成长,而不...

weixin_39619478的博客 2224

进程间通讯概述管道通讯信号通讯共享内存

为什么进程间需要通信? 1、数据传输:一个进程需要将它的数据发送给另一个进程。 2、资源共享:多个进程之间共享同样的资源。 3、通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。 4、进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。 Linux进程间通信(IPC)

宇宙的起源 428

进程间的通信方式(一)之——共享内存和信号量

本文详细介绍了进程间通信的两种方式:共享内存和信号量,对其应用场景和4(共享内存)+3(信号量)个接口函数进行了详细说明,并进行实战演练,通过运行结果更加直观的了解它们的通信机制。共享内存往往是结合信号量来实现进程间的同步和通信的,因此又给出例程阐述如何引入信号量来实现对共享资源的互斥访问。

weixin_45389997的博客 1832

进程通信--共享内存

<br /><br />定义:<br />共享内存,就是通过内核操作,在内存上开辟一块供多个进程共同访问的内存块。这块内存块的创建和<br />销毁是由内核来控制(当然,也可以在进程内部调用系统库函数来创建和销毁,类似于消息机制和信号<br />量机制)。在这个内存块上,进程可以像操作内存一样操作共享区内存。<br />作用:<br />第一个,就是提供进程间大信息量,而且快速的通信。这点也是大家第一反应想得到的。<br />第二个,除了提供最快速的进程间通信外,共享内存还有一个作用就是保存关键数据。在服务

利博瑞特 690

共享内存、消息队列、信号量

一般来说,共享内存、消息队列、信号量详解这三个技术,用的不多,简单认识了即可。如果想进一步了解,可以接着AI学习。

qq_51216031的博客 1163
上一篇: 核函数可达到的带宽
下一篇: 减少全局内存的访问
Fcw0551
博客等级 码龄2年 330粉丝 115原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值