尚学堂百战程序员:Java内存模型与线程

什么是Java内存模型? Java内存模型定义了线程之间共享的变量的访问规则。

如何区分主内存和工作内存? 主内存由所有线程共享,工作内存对于每个线程都是唯一的。

内存交互的8 个操作是什么? 锁定、解锁、读取、加载、使用、分配、存储、写入。

长波和双波有用吗? 这是没有用的,读写6 4 位数据可以分为两次3 2 位。

Java内存模型如何保证线程安全? 使用易失性、同步和并发实用程序类。

示例项目:某电商系统并发处理订单,并使用 volatile 关键字保证订单状态的原子性。

自己掂量一下。

「CUDA ON ARM」如何避免共享内存 Bank conflict

2 02 2 年……届时我们将举办CUDAonARM平台和在线训练营。
线程层次结构(thread hierarchy)非常混乱。

线程索引 (threadIdx) 是一个 3 D 向量。
可以修改 1 D、2 D 和 3 D 线程块。
线程索引直接链接到线程ID。
在一维块中,索引是标识符。
在 2 D 块中,那个公式...我后来意识到它是什么...[公式] 的线程标识符是 [公式]。
这同样适用于 3 D 块。
这需要考虑。

为了快速有效地运作。
内存块(threadblock)中的线程按ID分为3 2 个数字的组,称为warp。
Warp分为3 2 个操作核心。
半捻是指前半捻或后半捻。
这非常重要。

共享内存(shared memory),这个是同一个线程块中的线程共享的。
共享内存分为 3 2 个逻辑块(bank)。
不同的逻辑块可以同时被多个线程访问。
连续的 3 2 位内存访问被分为连续的逻辑块(bank)。
例如,__shared__floatsData[3 2 ][3 2 ],然后是 Bank[0] 中的 sData[0][0]、sData[1 ][0]...sData[3 1 ][0]。
sData[3 1 ] [0]、sData [3 1 ] [1 ]... sData [3 1 ] [3 1 ],在存储区 [3 1 ] 中。
你必须记住这一点。

银行冲突,这件事很烦人。
初步研究表明,无论哪种方式都不会发生银行冲突。
因此,设计原则是允许同一个warp中的不同线程访问不同bank中的数据。
这样,数据可以并行访问,但不能顺序访问。

如果同一warp中的不同线程必须访问同一bank中的数据,我们该怎么办?使用 MemoryPadding 进行优化。
允许同一warp中的线程访问不同bank中的数据。

CUDA:共享内存总结

简单来说,共享显存GPU的存储类型分为片内显存和片内显存。
共享内存因其低延迟和高带宽而成为首选。
使用共享内存的主要目的是减少对全局内存的访问,减少访问时间,提高性能。
我们先来说说最重要的事情:内存库的挣扎是性能提升的敌人。
例如,我们去年跑的项目大约大了3 000倍,由于内存条冲突,性能下降了2 0%。
其实很简单。
共享内存分为3 2 个内存库,对应线程扭曲中的3 2 个线程。
同时访问同一个内存条会造成冲突。
一开始我以为可以通过合理分配线程来避免冲突,但后来发现这是一个错误。
还有一件事。
按列读取时,同一列的元素都在同一个存储体中,这也会造成冲突。

为了避免这种冲突,我们使用填充技术。
例如,在5 个存储体中,当逐列读取第一列时,所有项目都在Bank0中,并且发生冲突。
这时,我们可以在最后一列填充一列占位内存,并将项目合并到不同的存储体中。
很多人不注意这一点。
填充的内存不存储项目,而仅充当占位符。

有两种填充方式:静态和动态。
静态打包适用于已知原始大小的共享内存,通过代码逻辑实现打包。
动态填充将线程号设置为如图1 所示的位置。
填充位无法被线程访问,从而实现矩阵置换,避免冲突。

综上所述,打包的关键是理解图1 和图2 之间的关系。
图1 并不位于计算机存储单元上,但它可以帮助我们更好地理解打包过程。
我认为值得一试,因为正确使用padding技术可以有效提高GPU性能。
等等,还有一件事,记得在编写程序时充分考虑到内存条冲突的可能性,避免性能损失。

jmm内存模型详解

不幸的是,JMM这个东西让我刚学多线程的时候很头疼。
但我告诉你,只有遇到陷阱你才会明白。

2 01 4 年在上海的时候,我写了一个后台任务和两个同时操作计数器的线程。
结果,数据不匹配。
查了半天,发现没有添加挥发性物质。
你看,这是一个可见性的问题。
添加挥发物后,嘿,很快就准备好了。
这提醒我,公共变量不是易失性的,本质上只是猜测。

还有一次,2 01 6 年,我们在深圳举办了快闪活动。
要求是每秒处理5 00个订单。
一开始我用的是同步锁,但是系统直接崩溃了。
然后改成ReentrantLock,结合条件变量,就搞定了。
这让我明白,就原子性而言,同步并不是所有情况下的最优解决方案。
有时使用 AtomicInteger 效率更高。

但是,JMM,我真的必须调查一下。
我老了,记不起那些指示了。
我所知道的是,波动性是一件好事,但不要指望它能完成同步工作。
最后一个区域也很有趣。
我已经看到最终字段在构造函数中被分配了一个值。
当对象被释放时,所有线程都可以看到它。
这个顺序相当不错。

归根结底,JMM是一种规范。
每个 JVM 制造商都有自己的实现。
就像 HotSpot 一样,您可以使用它,但不要太认真。
您是在问字符串私有变量吗?你当然不用担心这个,只要照顾好你自己的三分之一英亩就可以了。

如果你编写代码,请记住以下几点:共享变量由易失性修改,关联操作使用同步或原子性,并且不要使用易失性作为原子锁。
通过理解这些,大多数并发问题都可以被忽略。
好了,我不跟你废话了,开始干活了。