1.PyTorch 源码解读之 BN & SyncBN:BN 与 多卡同步 BN 详解
2.golang的源码对象池sync.pool源码解读
3.onnxruntime源码学习-编译与调试 (公网&内网)
4.Go并发编程之原子操作sync/atomic
5.Go并åç¼ç¨ â sync.Once
6.Golang sync.Cond 条件变量源码分析
PyTorch 源码解读之 BN & SyncBN:BN 与 多卡同步 BN 详解
BatchNorm原理 BatchNorm最早在全连接网络中提出,旨在对每个神经元的推荐输入进行归一化操作。在卷积神经网络(CNN)中,源码这一原理被扩展为对每个卷积核的推荐输入进行归一化,即在channel维度之外的源码所有维度上进行归一化。BatchNorm带来的推荐顶级操盘线主图源码通达信优势包括提高网络的收敛速度、稳定训练过程、源码减少过拟合现象等。推荐 BatchNorm的源码数学表达式为公式[1],引入缩放因子γ和移位因子β,推荐作者在文章中解释了它们的源码作用。 PyTorch中与BatchNorm相关的推荐类主要位于torch.nn.modules.batchnorm模块中,包括如下的源码类:_NormBase、BatchNormNd。推荐 具体实现细节如下: _NormBase类定义了BN相关的源码一些属性。 初始化过程。 模拟BN的forward过程。 running_mean、running_var的更新逻辑。 γ、β参数的更新方式。 BN在eval模式下的行为。 BatchNormNd类包括BatchNorm1d、BatchNorm2d、BatchNorm3d,它们的区别在于检查输入的合法性,BatchNorm1d接受2D或3D的输入,BatchNorm2d接受4D的输入,BatchNorm3d接受5D的输入。 接着,介绍SyncBatchNorm的实现。 BN性能与batch size密切相关。android 阅读 源码分析在batch size较小的场景中,如检测任务,内存占用较高,单张显卡难以处理较多,导致BN效果不佳。SyncBatchNorm提供了解决方案,其原理是所有计算设备共享同一组BN参数,从而获得全局统计量。 SyncBatchNorm在torch/nn/modules/batchnorm.py和torch/nn/modules/_functions.py中实现,前者负责输入合法性检查以及参数设置,后者负责单卡统计量计算和进程间通信。 SyncBatchNorm的forward过程。 复习方差计算方式。 单卡计算均值、方差,进行归一化处理。 同步所有卡的数据,得到全局均值mean_all和逆标准差invstd_all,计算全局统计量。 接着,介绍SyncBatchNorm的backward过程。 在backward过程中,需要在BN前后进行进程间通信。这在_functions.SyncBatchNorm中实现。 计算weight、bias的梯度以及γ、β,进一步用于计算梯度。golang的对象池sync.pool源码解读
Go语言对象池sync.pool源码深度解析
对象池在Go语言中被设计用于解决频繁创建和销毁对象导致的性能问题。sync.pool的核心理念是复用已创建对象,减轻垃圾收集(GC)压力。hex文件解析源码以下是关键点的理解和代码分析:对象池的动机
新对象的创建会消耗内存,并可能对GC造成负担。sync.pool就是为了解决这个问题,通过预先创建和存储对象,减少创建成本,提高性能。池与缓存的相似性
无论是连接池、线程池还是对象池,它们都体现了池化和缓存的思想:复用资源,减少临时创建,提升响应速度。池化和缓存都是为了减少资源消耗,提升服务效率。go1.原理与用法
对象池使用简单,通过New函数创建,Get和Put操作实现对象的复用。go1.之前的版本可能频繁清空池,导致性能损失。1.改进了设计,引入了victim cache机制,通过双向链表优化获取和存储对象,减少锁竞争。源码解析
从pool的结构体可以看到,victim和victimSize用于管理受害缓存,popTail函数通过无锁操作处理链表,保证了高性能。put操作时,根据对象状态决定放入private或shared区域。总结
对象池通过复用对象、提前准备和性能优化的存储提高性能。理解对象池的c简单扫雷源码关键在于:复用、存储策略和并发控制。在Go 1.中,通过victim cache和链表操作,进一步提升了性能和并发处理能力。深入理解
理解对象池的细节包括如何禁用抢占P以防止GC影响,以及如何通过noCopy防止对象拷贝导致的潜在问题。同时,伪共享的处理也是优化对象池性能的关键点。 持续学习和实践是技术成长的基石,让我们保持对技术的热情,不断探索和优化。onnxruntime源码学习-编译与调试 (公网&内网)
在深入学习ONNX Runtime的过程中,我决定从1.版本开始,以对比与理解多卡并行技术。为此,我选择了通过`./tools/ci_build/build.py`脚本进行编译,而不是直接执行`build.sh`,因为后者并不直接提供所需的参数。在`build.py:::parse_arguments()`函数中,我找到了可选择的参数,例如运行硬件(CPU/GPU)、调试模式(Debug/Release)以及是否并行编译。我特别使用了`--skip_submodule_sync`,以避免因与公网不通而手动下载“submodule”,即`./cmake/external`文件夹下的依赖组件。这样可以节省每次编译时检查依赖组件更新的时间,提高编译效率。同时,我使用`which nvcc`命令来确定`cuda_home`和`cudnn_home`的值。
我的编译环境配置为gcc8.5.0、cuda.7和cmake3..1,eclipse tomcat 源码下载其中cmake版本需要不低于3.,gcc版本则至少为7.0,否则编译过程中会出现错误。在编译环境的配置中,可以通过设置PATH和LD_LIBRARY_PATH来指定可执行程序和动态库的路径。对于手动下载“submodule”的不便,可以通过先在公网编译cpu版本,然后在编译开始阶段由构建脚本自动下载所有依赖组件并拷贝至所需目录来简化流程。
编译顺利完成后,生成的so文件并未自动放入bin目录,这可能是由于在安装步骤后bin目录下才会出现相应的文件。接下来,我进入了调试阶段,使用vscode进行调试,最终成功运行了`build/RelWithDebInfo/onnxruntime_shared_lib_test`可执行文件。
在深入研究ONNX Runtime的编译流程时,我发现了一个更深入的资源,它涵盖了从`build.sh`到`build.py`再到`CmakeList.txt`的编译过程,以及上述流程中涉及的脚本解析。对这个流程感兴趣的读者可以进行更深入的研究。
在编译过程中,我遇到了一些问题,如下载cudnn并进行安装,以及解决找不到`stdlib.h`的问题。对于找不到`stdlib.h`,我通过查阅相关文章和理解编译过程中搜索路径的逻辑,最终找到了解决方案。如果忽略这个问题,我选择在另一台机器上重新编译以解决问题。
在使用vscode调试时,我遇到了崩溃问题,这可能是由于vscode、gdb或Debug模式编译出的可执行文件存在潜在问题。通过逐步排除,我最终确定问题可能出在Debug模式编译的可执行文件上。这一系列的探索和解决过程,不仅加深了我对ONNX Runtime的理解,也提高了我的调试和问题解决能力。
Go并发编程之原子操作sync/atomic
Go语言的并发编程中,sync/atomic包提供了底层的原子内存操作,用于处理并发环境中的数据同步和冲突避免。这个包利用了CPU的原子操作指令,确保在并发情况下,对变量的操作是线程安全的。然而,官方建议仅在必要且确实涉及底层操作时使用,如避免使用channel或sync包中的锁的场景。
sync/atomic包的核心是5种基本数据类型的原子操作:add(只支持int、int、uint、uint和uintptr),以及一个扩展的Value类型,后者在1.4版本后支持Load、Store、CompareAndSwap和Swap方法,可用于操作任意类型的数据。Value类型尤其重要,因为它扩展了原子操作的适用范围。
具体来说,swap操作(如SwapInt)用于原子地替换内存中的值,compare-and-swap(CAS)则检查并替换值,如果当前值与预期值一致。add操作(如AddInt)则进行加法操作并返回新值,而load、store操作分别用于读取和写入值,如LoadInt和StoreInt。
在实际使用时,例如对map的并发读写,可以通过Value类型避免加锁。sync/atomic的相关源码和示例可在GitHub的教程[1]和作者的个人网站[2]中找到。至于进一步学习,可以关注公众号coding进阶获取更多资源,或者在知乎[3]上查找无忌的资料。
参考资料:
[1] Go语言初级、中级和高级教程: github.com/jincheng9/go...
[2] Jincheng's Blog: jincheng9.github.io/
[3] 无忌: zhihu.com/people/thucuh...
Go并åç¼ç¨ â sync.Once
ç®ä»
Once å¯ä»¥ç¨æ¥æ§è¡æ个å½æ°ï¼ä½æ¯è¿ä¸ªå½æ°ä» ä» åªä¼æ§è¡ä¸æ¬¡ï¼å¸¸å¸¸ç¨äºåä¾å¯¹è±¡çåå§ååºæ¯ã说å°è¿ï¼å°±ä¸å¾ä¸è¯´ä¸ä¸åä¾æ¨¡å¼äºã
åä¾æ¨¡å¼åä¾æ¨¡å¼æææ±å¼å饿æ±å¼ä¸¤ç§ï¼ä¸ä»£ç ã
饿æ±å¼é¥¿æ±å¼é¡¾åæä¹å°±æ¯æ¯è¾é¥¥é¥¿ï¼æ以就æ¯ä¸æ¥å°±åå§åã
var?instance?=?&Singleton{ }type?Singleton?struct?{ }func?GetInstance()?*Singleton?{ return?instance}ææ±å¼ææ±å¼é¡¾åæä¹å°±æ¯å·æï¼å¨è·åå®ä¾çæ¶åå¨è¿è¡åå§åï¼ä½æ¯ææ±å¼ä¼æ并åé®é¢ã并åé®é¢ä¸»è¦åçå¨ instance == nil è¿ä¸ªå¤ææ¡ä»¶ä¸ï¼æå¯è½å¤ä¸ª goruntine åæ¶è·å instance 对象é½æ¯ nil ï¼ç¶åé½å¼å§åå»ºäº Singleton å®ä¾ï¼å°±ä¸æ»¡è¶³åä¾æ¨¡å¼äºã
var?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ if?instance?==?nil?{ ?instance?=?&Singleton{ }}return?instance}å éæ们é½ç¥é并åé®é¢åºç°åï¼å¯ä»¥éè¿å éæ¥è¿è¡è§£å³ï¼å¯ä»¥ä½¿ç¨ sync.Metux æ¥å¯¹æ´ä¸ªæ¹æ³è¿è¡å éï¼å°±ä¾å¦ä¸é¢è¿æ ·ãè¿ç§æ¹å¼æ¯è§£å³äºå¹¶åçé®é¢ï¼ä½æ¯éçç²åº¦æ¯è¾é«ï¼æ¯æ¬¡è°ç¨ GetInstance æ¹æ³çæ¶åé½éè¦è·å¾éæè½è·å¾ instance å®ä¾ï¼å¦æå¨è°ç¨é¢çæ¯è¾é«çåºæ¯ä¸æ§è½å°±ä¸ä¼å¾å¥½ãé£æä»ä¹æ¹å¼å¯ä»¥è§£å³åï¼è®©æ们æ¥çå¾ä¸çå§
var?mutex?sync.Mutexvar?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ mutex.Lock()defer?mutex.Unlock()if?instance?==?nil?{ ?instance?=?&Singleton{ }}return?instance}Double Check为äºè§£å³éçç²åº¦é®é¢ï¼æ们å¯ä»¥ä½¿ç¨ Double Check çæ¹å¼æ¥è¿è¡è§£å³ï¼ä¾å¦ä¸é¢ç代ç ï¼ç¬¬ä¸æ¬¡å¤æ instance == nil ä¹åéè¦è¿è¡å éæä½ï¼ç¶åå第äºæ¬¡å¤æ instance == nil ä¹åæè½å建å®ä¾ãè¿ç§æ¹å¼å¯¹æ¯ä¸é¢çæ¡ä¾æ¥è¯´ï¼éçç²åº¦æ´ä½ï¼å 为å¦æ instance != nil çæ åµä¸æ¯ä¸éè¦å éçãä½æ¯è¿ç§æ¹å¼å®ç°èµ·æ¥æ¯ä¸æ¯æ¯è¾éº»ç¦ï¼æ没æä»ä¹æ¹å¼å¯ä»¥è§£å³å¢ï¼
var?mutex?sync.Mutexvar?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ if?instance?==?nil?{ ?mutex.Lock()?defer?mutex.Unlock()?if?instance?==?nil?{ ?instance?=?&Singleton{ }?}}return?instance}ä½¿ç¨ sync.Onceå¯ä»¥ä½¿ç¨ sync.Once æ¥å®ç°åä¾çåå§åé»è¾ï¼å 为è¿ä¸ªé»è¾è³å¤åªä¼è·ä¸æ¬¡ãæ¨è使ç¨è¿ç§æ¹å¼æ¥è¿è¡åä¾çåå§åï¼å½ç¶ä¹å¯ä»¥ä½¿ç¨é¥¿æ±å¼ã
var?once?sync.Oncevar?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ once.Do(func()?{ ?instance?=?&Singleton{ }})return?instance}æºç åæä¸é¢å°±æ¯ sync.Once å çæºç ï¼æå é¤äºæ³¨éï¼ä»£ç ä¸å¤ï¼Once æ°æ®ç»æ主è¦ç± done å m ç»æï¼å ¶ä¸ done æ¯åå¨ f å½æ°æ¯å¦å·²æ§è¡ï¼m æ¯ä¸ä¸ªéå®ä¾ã
type?Once?struct?{ done?uint?//?få½æ°æ¯å¦å·²æ§è¡mMutex?//?é}func?(o?*Once)?Do(f?func())?{ if?atomic.LoadUint(&o.done)?==?0?{ ?o.doSlow(f)}}func?(o?*Once)?doSlow(f?func())?{ o.m.Lock()defer?o.m.Unlock()if?o.done?==?0?{ ?defer?atomic.StoreUint(&o.done,?1)?f()}}Do æ¹æ³
ä¼ å ¥ä¸ä¸ª functionï¼ç¶å sync.Once æ¥ä¿è¯åªæ§è¡ä¸æ¬¡ï¼å¨ Do æ¹æ³ä¸ä½¿ç¨ atomic æ¥è¯»å done åéï¼å¦ææ¯ 0 ï¼å°±ä»£ç f å½æ°æ²¡æ被æ§è¡è¿ï¼ç¶åå°±è°ç¨ doSlowæ¹æ³ï¼ä¼ å ¥ f å½æ°
doShow æ¹æ³
doShow ç第ä¸ä¸ªæ¥éª¤å°±æ¯å å éï¼è¿éå éçç®çæ¯ä¿è¯åä¸æ¶å»æ¯è½ç±ä¸ä¸ª goruntine æ¥æ§è¡ doSlow æ¹æ³ï¼ç¶åå次å¤æ done æ¯å¦æ¯ 0 ï¼è¿ä¸ªå¤æå°±ç¸å½äºæ们ä¸é¢è¯´ç DoubleCheck ï¼å 为 doSlow å¯è½åå¨å¹¶åé®é¢ãç¶åæ§è¡ f æ¹æ³ï¼ç¶åæ§è¡ä½¿ç¨ atomic å° done ä¿åæ 1ãä½¿ç¨ DoubleCheck ä¿è¯äº f æ¹æ³åªä¼è¢«æ§è¡ä¸æ¬¡ã
æ¥ççï¼é£å¯ä»¥è¿æ ·å®ç° sync.Once åï¼
è¿æ ·ä¸æ¯æ´ç®åä¸ç¹åï¼ä½¿ç¨ååç CAS æä½å°±å¯ä»¥è§£å³å¹¶åé®é¢åï¼å¹¶ååªæ§è¡ä¸æ¬¡ f æ¹æ³çé®é¢æ¯å¯ä»¥è§£å³ï¼ä½æ¯ Do æ¹æ³å¯è½å¹¶åï¼ç¬¬ä¸ä¸ªè°ç¨è å° done 设置æäº 1 ç¶åè°ç¨ f æ¹æ³ï¼å¦æ f æ¹æ³ç¹å«èæ¶é´ï¼é£ä¹ç¬¬äºä¸ªè°ç¨è è·åå° done 为 1 å°±ç´æ¥è¿åäºï¼æ¤æ¶ fæ¹æ³æ¯æ²¡ææ§è¡è¿ç¬¬äºæ¬¡ï¼ä½æ¯æ¤æ¶ç¬¬äºä¸ªè°ç¨è å¯ä»¥ç»§ç»æ§è¡åé¢ç代ç ï¼å¦æåé¢ç代ç ä¸æç¨å° f æ¹æ³å建çå®ä¾ï¼ä½æ¯ç±äº f æ¹æ³è¿å¨æ§è¡ä¸ï¼æ以å¯è½ä¼åºç°æ¥éé®é¢ãæ以å®æ¹éç¨çæ¯Lock + DoubleCheck çæ¹å¼ã
if?atomic.CompareAndSwapUint(&o.done,?0,?1)?{ f()}æå±æ§è¡å¼å¸¸åå¯ç»§ç»æ§è¡çOnce
çæäºæºç ä¹åï¼æ们就å¯ä»¥æ©å± sync.Once å äºãä¾å¦ f æ¹æ³å¨æ§è¡çæ¶åæ¥éäºï¼ä¾å¦è¿æ¥åå§å失败ï¼æä¹åï¼æ们å¯ä»¥å®ç°ä¸ä¸ªé«çº§çæ¬ç Once å ï¼å ·ä½ç slowDo 代ç å¯ä»¥åèä¸é¢çå®ç°
func?(o?*Once)?slowDo(f?func()?error)?error?{ o.m.Lock()defer?o.m.Unlock()var?err?errorif?o.done?==?0?{ ?//?Double?Checkerr?=?f()if?err?==?nil?{ ?//?没æå¼å¸¸çæ¶åè®°å½doneå¼atomic.StoreUint(&o.done,?1)}}return?err}带æ§è¡ç»æç Once
ç±äº Once æ¯ä¸å¸¦æ§è¡ç»æçï¼æ们ä¸ç¥é Once ä»ä¹æ¶åä¼æ§è¡ç»æï¼å¦æåå¨å¹¶åï¼éè¦ç¥éæ¯å¦æ§è¡æåçè¯ï¼å¯ä»¥çä¸ä¸é¢çæ¡ä¾ï¼æè¿éæ¯ä»¥ redis è¿æ¥çé®é¢æ¥è¿è¡è¯´æçãDo æ¹æ³æ§è¡å®æ¯åå° init å¼è®¾ç½®æ 1 ï¼ç¶åå ¶ä» goruntine å¯ä»¥éè¿ IsConnetion æ¥è·åè¿æ¥æ¯å¦å»ºç«ï¼ç¶åååç»çæä½ã
type?RedisConn?struct?{ once?sync.Onceinit?uint}func?(this?*RedisConn)?Init()?{ this.once.Do(func()?{ ?//?do?redis?connection?atomic.StoreUint(&this.init,?1)})}func?(this?*RedisConn)?IsConnect()?bool?{ ?//?å¦å¤ä¸ä¸ªgoroutinereturn?atomic.LoadUint(&this.init)?!=?0}Golang sync.Cond 条件变量源码分析
sync.Cond 是 Golang 标准库 sync 包中一个关键的条件变量类型,用于在多个goroutine间协调等待特定条件。它常用于生产者-消费者模型等场景,确保在某些条件满足后才能继续执行。本文基于 go-1. 源码,深入解析 sync.Cond 的核心机制与用法。
sync.Cond 的基本用法包括创建条件变量、等待唤醒与发送信号。使用时,通常涉及到一个互斥锁(Locker)以确保并发安全性。首先,通过`sync.NewCond(l Locker)`创建条件变量。其次,`cond.Wait()`使当前执行的goroutine等待直到被唤醒,期间会释放锁并暂停执行。`cond.Signal()`和`Broadcast()`用于唤醒等待的goroutine,前者唤醒一个,后者唤醒所有。
在底层实现中,sync.Cond 采用了一种称为 notifyList 的数据结构来管理等待和唤醒过程。notifyList 由一组元素构成,其中`wait`和`notify`表示当前最大ticket值和已唤醒的最大ticket值,而`head`和`tail`则分别代表等待的goroutine链表的头和尾。在`Wait`操作中,每次调用`runtime_notifyListAdd`生成唯一的ticket,并将当前goroutine添加到链表中。当调用`Signal`或`Broadcast`时,会查找并唤醒当前`notify`值对应的等待goroutine,并更新`notify`值。
信号唤醒过程确保了FIFO的顺序,即最早等待的goroutine会首先被唤醒。这种机制有效地防止了并发操作下列表的乱序,确保了正确的唤醒顺序,尽管在实际执行中,遍历整个列表的过程在大多数情况下效率较高。
在使用sync.Cond时,需注意避免潜在的死锁风险和错误的唤醒顺序。确保合理管理互斥锁的使用,以及在适当情况下使用`Signal`或`Broadcast`来唤醒等待的goroutine。正确理解和应用sync.Cond,能有效提升并发编程的效率与稳定性。
深度解析sync WaitGroup源码
waitGroup
waitGroup 是 Go 语言中并发编程中常用的语法之一,主要用于解决并发和等待问题。它是 sync 包下的一个子组件,特别适用于需要协调多个goroutine执行任务的场景。
waitGroup 主要用于解决goroutine间的等待关系。例如,goroutineA需要在等待goroutineB和goroutineC这两个子goroutine执行完毕后,才能执行后续的业务逻辑。通过使用waitGroup,goroutineA在执行任务时,会在检查点等待其他goroutine完成,确保所有任务执行完毕后,goroutineA才能继续进行。
在实现上,waitGroup 通过三个方法来操作:Add、Done 和 Wait。Add方法用于增加计数,Done方法用于减少计数,Wait方法则用于在计数为零时阻塞等待。这些方法通过原子操作实现同步安全。
waitGroup的源码实现相对简洁,主要涉及数据结构设计和原子操作。数据结构包括了一个 noCopy 的辅助字段以及一个复合意义的 state1 字段。state1 字段的组成根据目标平台的不同(位或位)而有所不同。在位环境下,state1的第一个元素是等待线程数,第二个元素是 waitGroup 计数值,第三个元素是信号量。而在位环境下,如果 state1 的地址不是位对齐的,那么 state1 的第一个元素是信号量,后两个元素分别是等待线程数和计数值。
waitGroup 的核心方法 Add 和 Wait 的实现原理如下:
Add方法通过原子操作增加计数值。当执行 Add 方法时,首先将 delta 参数左移位,然后通过原子操作将其添加到计数值上。需要注意的是,delta 的值可正可负,用于在调用 Done 方法时减少计数值。
Done方法通过调用 Add(-1)来减少计数值。
Wait方法则持续检查 state 值。当计数值为零时,表示所有子goroutine已完成,调用者无需等待。如果计数值大于零,则调用者会变成等待者,加入等待队列,并阻塞自己,直到所有任务执行完毕。
通过使用waitGroup,开发者可以轻松地协调和同步并发任务的执行,确保所有任务按预期顺序完成。这在多goroutine协同工作时,尤其重要。掌握waitGroup的使用和源码实现,将有助于提高并发编程的效率和可维护性。
如果您对并发编程感兴趣,希望持续关注相关技术更新,请通过微信搜索「迈莫coding」,第一时间获取更多深度解析和实战指南。