条款04:确定对象被使用前已先被初始化(Make sure that objects are initialized before they're used.)

本文讨论了C++中对象初始化的基本原则,包括内置类型的手动初始化、构造函数中的成员初始化、静态对象的生命周期与初始化次序等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. “将对象初始化”这事,C++似乎反复无常。

 

在某些语境下x保证被初始化(为0),p的成员变量有时候被初始化(为0),有时候不会。

读取未初始化的值会导致不明确的行为。

 

2. 永远在使用对象之前先将它初始化

对内置类型,手动初始化。

至于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上。规则很简单:确保每一个构造函数都将对象的每一个成员初始化。

这个规则很容易奉行,重要的是别混淆了赋值(assignment )和初始化( initialization )。

C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。

构造函数的一个较佳写法是,使用所谓的member initialization list(成员初值列)替换赋值动作:

 

好的习惯:总是在初值列中列出所有成员变量(如果成员变量是const或references,它们就一定需要初值,不能被赋值)。

classes拥有多个构造函数,每个构造函数有自己的成员初值列。这种情况下可以合理地在初值列中遗漏那些“赋值表现像初始化一样好”的成员变量,改用它们的赋值操作,并将那些赋值操作移往某个函数(通常是private,供所有构造函数调用。

 

3. 成员初始化次序

次序总是相同:base classes更早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化。

当你在成员初值列中条列各个成员时,最好总是以其声明次序为次序。

 

4. static对象

所谓static对象,其寿命从被构造出来直到程序结束为止,因此stack和heap-based对象都被排除。这种对象包括global对象、定义于namespace作用域内的对象、在classes内、在函数内、以及在file作用域内被声明为static的对象。

函数内的static对象称为local static对象(因为它们对函数而言是local),其他static对象称为non-local static对象。程序结束时static对象会被自动销毁,也就是它们的析构函数会在main ( )结束时被自动调用。

 

5. non-local static对象的初始化次序。

C++对“定义于不同编译单元内的non-local static对象的初始化次序并无明确定义。

将每个non-local static对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference指向它所含的对象。换句话说,non-local static对象被local static对象替换了。

这是Singleton模式的一个常见实现手法。

这个手法的基础在于:C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。所以如果你以“函数调用”(返回一个reference指向local static对象)替换“直接访问non-local static对象”。

 

此种方式适合inline(频繁调用,短)

但是从另一个角度看,这些函数“内含static对象”的事实使它们在多线程系统中带有不确定性。再说一次,任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。处理这个麻烦的一种做法是:在程序的单线程启动阶段( single-threaded startup portion)手工调用所有reference-returning函数,这可消除与初始化有关的,’竞速形势(race conditions )。

 

6. 总结

1)为内置型对象进行手工初始化,因为c什不保证初始化它们。

2)构造函数最好使用成员初值列(member initialization list ),而不要在构造函数本体内使用赋值操作(assignment )。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。

3)为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。

 

<think>我们正在解决用户在使用`tune.run`时遇到的RuntimeError:'Defaultprocessgrouphasnotbeeninitialized'。这个错误通常与分布式训练有关,表明在尝试使用分布式进程组之没有正确初始化。根据引用[1],在服务器多卡训练时出现这个错误是因为分布式训练的问题,具体是默认进程组没有被初始化。因此,我们需要确保在训练开始调用`init_process_group`。引用[2]提供了关于内核参数传递的信息,但似乎与当问题关联不大,因此我们主要关注引用[1]的提示。解决方案的核心在于正确初始化进程组。在PyTorch分布式训练中,我们通常使用`torch.distributed.init_process_group`来初始化进程组。在使用RayTune的`tune.run`时,我们需要确保在每个训练进程(可能运行在不同的节点上)中正确初始化分布式环境。具体步骤可能包括:1.在每个训练任务开始时,初始化进程组。2.使用正确的后端(如gloo、nccl)和初始化方法(如env://)。3.注意在RayTune的上下文中,每个trial可能在一个单独的进程中运行,因此需要为每个trial初始化进程组。然而,在RayTune中,如果我们使用PyTorch的分布式训练(如DistributedDataParallel),我们需要使用RayTune提供的集成工具,例如使用`ray.tune.integration.torch.DistributedTrainableCreator`来包装训练函数,或者使用`tune.run`的`num_workers_per_trial`参数来指定每个trial的worker数量(即每个trial的进程数),并在训练函数内部初始化进程组。下面是一个示例解决方案:###解决方案在使用`tune.run`运行分布式训练时,需要确保在每个训练进程中初始化进程组。我们可以通过以下步骤实现:1.在训练函数内部,根据当进程的排名等信息初始化进程组。2.使用RayTune的`DistributedTrainableCreator`来创建分布式训练对象,或者使用`num_workers_per_trial`参数并设置正确的初始化。这里给出一个使用`DistributedTrainableCreator`的示例:```pythonimporttorchimporttorch.distributedasdistfromrayimporttunefromray.tune.integration.torchimportDistributedTrainableCreatordeftrain_func(config):#获取当进程的信息rank=dist.get_rank()#注意:在初始化无法调用get_rank,所以我们需要通过其他方式获得rank#实际上,在RayTune的分布式训练中,rank通常通过环境变量获取#建议使用RAY_WORKER_RANK环境变量,但更标准的做法是使用RayTune提供的信息#通常,我们通过环境变量来设置#在RayTune中,每个worker会自动设置环境变量RANK,WORLD_SIZE等#所以我们可以这样初始化:dist.init_process_group(backend="gloo",#或者"nccl",根据你的硬件选择init_method="env://",#不需要显式提供rank和world_size,因为它们会从环境变量中读取)#之后进行正常的训练流程#...你的模型、优化器、数据加载等#注意:数据加载可能需要使用DistributedSampler#训练完成后,销毁进程组dist.destroy_process_group()#使用DistributedTrainableCreator包装训练函数trainable_cls=DistributedTrainableCreator(train_func,num_workers=4,#每个trial的worker数量,即每个trial的进程数num_cpus_per_worker=1,num_gpus_per_worker=1,#如果每个worker需要GPUbackend="gloo"#这里指定后端,但实际在train_func中初始化时也要指定)#然后运行tunetune.run(trainable_cls,config={},#你的配置num_samples=1#样本数(即trial数))```注意:上面的示例中,我们在`train_func`内部初始化了进程组。`DistributedTrainableCreator`会负责设置必要的环境变量(如RANK,WORLD_SIZE,MASTER_ADDR,MASTER_PORT),所以我们在初始化进程组时使用`init_method="env://"`即可。###另一种方法:使用`num_workers_per_trial`另一种方式是使用`tune.run`的`num_workers_per_trial`参数,并配合在训练函数中初始化进程组。但是,注意这种方式需要你手动处理进程组初始化,并且确保每个worker有正确的环境变量设置。RayTune在启动多个worker时也会设置相应的环境变量(如RANK,WORLD_SIZE),因此我们同样可以使用环境变量初始化。```pythondeftrainable(config):#初始化进程组dist.init_process_group(backend="gloo",init_method="env://")#...训练代码#在tune.run中指定tune.run(trainable,config={},num_workers_per_trial=4,#每个trial启动4个workerresources_per_trial={"cpu":4,"gpu":4}#根据实际情况调整)```###注意事项1.**后端选择**:在CPU上运行使用gloo,在GPU上运行使用nccl通常更好。2.**环境变量**:确保使用`init_method="env://"`,这样PyTorch会从环境变量中读取`RANK`、`WORLD_SIZE`、`MASTER_ADDR`和`MASTER_PORT`。RayTune的分布式训练工具会自动设置这些环境变量。3.**端口冲突**:如果多个trial在同一节点上运行,可能会因为使用相同的端口号而冲突。RayTune会自动分配不同的端口以避免冲突。如果按照上述步骤仍然遇到问题,请检查环境变量是否正确设置,以及后端是否适合你的硬件。###相关问题1.在RayTune中如何为每个trial分配不同的GPU?2.如何在使用分布式训练时设置不同的学习率调整策略?3.在分布式训练中如何处理数据加载和分布式采样?希望这些信息能帮助你解决问题。</think>### RuntimeError: 'Default process group has not been initialized' 解决方案 在使用 `tune.run` 进行分布式训练时遇到 `RuntimeError: Default process group has not been initialized` 错误,根本原因是 PyTorch 分布式环境未正确初始化。以下是系统性的解决方案: --- #### **1. 手动初始化进程组** 在训练函数开头显式调用 `init_process_group`: ```python import torch.distributed as dist def train_func(config): # 初始化分布式进程组 dist.init_process_group( backend="nccl" if torch.cuda.is_available() else "gloo", init_method="env://" ) # 后续训练代码... ``` **关键参数说明**: - `backend`:GPU 集群用 `nccl`,CPU 集群用 `gloo` - `init_method="env://"`:从环境变量自动获取 `MASTER_ADDR` 和 `MASTER_PORT` --- #### **2. 确保环境变量正确设置** Ray Tune 会自动设置分布式环境变量,但需验证: ```python import os print("RANK:", os.environ.get("RANK")) # 应输出 0,1,2... print("WORLD_SIZE:", os.environ.get("WORLD_SIZE")) # 总进程数 ``` --- #### **3. 使用 Ray Tune 分布式包装器** 通过 `DistributedTrainableCreator` 自动处理初始化: ```python from ray.tune.integration.torch import DistributedTrainableCreator trainable_cls = DistributedTrainableCreator( train_func, # 你的训练函数 num_workers=4, # 每个 trial 的进程数 num_cpus_per_worker=1, num_gpus_per_worker=0.5 # 按需分配 GPU ) tune.run(trainable_cls, num_samples=10) ``` --- #### **4. 检查端口冲突** 若出现 `Address already in use` 错误,指定不同端口: ```python # 在初始化设置环境变量 os.environ["MASTER_PORT"] = "29500" # 改为未占用的端口 ``` --- #### **5. 验证 NCCL/Gloo 后端兼容性** - **NCCL 验证**:运行 `torch.cuda.nccl.version()` 确认 NCCL 已安装 - **Gloo 回退**: ```python dist.init_process_group(backend="gloo") # 强制 CPU 后端 ``` --- ### 完整示例代码 ```python import torch import torch.distributed as dist from ray import tune from ray.tune.integration.torch import DistributedTrainableCreator def train_func(config): # 初始化进程组 dist.init_process_group(backend="nccl") rank = dist.get_rank() print(f"Rank {rank} started training") # 此处插入实际训练代码 # ... # 创建分布式训练对象 trainable = DistributedTrainableCreator( train_func, num_workers=2, # 2 个并行进程 num_gpus_per_worker=0.5 ) # 启动 Tune 实验 tune.run( trainable, config={"lr": tune.grid_search([0.01, 0.001])} ) ``` ### 注意事项 1. **GPU 分配**:确保 `num_gpus_per_worker` ≤ 物理 GPU 数量 2. **版本兼容**: - PyTorch ≥ 1.9 - Ray ≥ 1.10 3. **防火墙**:开放节点间的通信端口(默认 29500) > 引用说明:分布式训练需显式初始化进程组,否则会触发 `RuntimeError`[^1];环境变量 `MASTER_PORT` 等需正确配置以避免端口冲突[^2]。 --- ### 相关问题 1. 如何在多节点集群上配置 Ray Tune 的分布式训练? 2. PyTorch 分布式训练中 `nccl` 和 `gloo` 后端的主要区别是什么? 3. 如何监控 Ray Tune 分布式训练中的 GPU 资源利用率? 4. 当训练进程意外退出时,如何自动恢复 Ray Tune 实验? 5. 如何为不同的 trial 设置差异化的分布式训练参数?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值