You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Acenet Siku集群中R并行处理adehabitatHR的内存管理问询

问题描述

背景

使用adehabitatHR包生成野生动物种群的利用分布估计(UDEs),需处理数千个UDEs,通过terra包的vect()函数转换为Shapefile,本地单线程处理代码为:

spatvec_UDEs <- lapply(UDEs, function(x){vect(x)})

核心问题

在Acenet Siku集群上并行执行家域估计算法时,内存占用极高且无法成功保存/导出结果;对SLURM内存映射逻辑、共享/分布式内存的区别及在SLURM中的运行逻辑存在困惑,尝试多种并行方式(parLapply()、mclapply()、slurm_map)及SLURM交互式命令均失败。

已尝试的SLURM交互式命令及问题

BRB1

salloc --time=3:00:0 --ntasks=1 --cpus-per-task=8 --mem-per-cpu=23875M

使用mclapply时出现无限重复的"Selection"界面。

BRB2

salloc --time=3:00:0 --ntasks=1 --cpus-per-task=16 --mem-per-cpu=11937M

耗时4619秒,parLapply无法识别有效集群。

BRB3

salloc --time=3:00:0 --nodes=1 --ntasks=1 --cpus-per-task=16 --mem-per-cpu=11937M

耗时4700秒(比BRB2慢),添加cl=cl参数后parLapply执行失败。

BRB4

salloc --time=3:00:0 --nodes=1 --cpus-per-task=20 --mem-per-cpu=11937M

耗时4262.420秒(最快),但parLapply执行失败。

BRB5

salloc --time=3:00:0 --nodes=1 --cpus-per-task=20 --mem-per-cpu=18500M

耗时4565.692秒,增加内存未提升速度;尝试slurm_map提交作业失败:

sbatch: error: Batch job submission failed: Requested partition configuration not available now.

最小可复现示例(保存为Rds)

SLURM命令

salloc --time=3:00:0 --nodes=2 --cpus-per-task=20 --mem-per-cpu=18500M

R代码

library('adehabitatHR')
library('foreach')

data(puechcirc)
pc1 <- puechcirc[1]
pc2 <- puechcirc[2]
pc3 <- puechcirc[3]
Traj_li <- vector("list", 3)
Traj_li[[1]] <- pc1
Traj_li[[2]] <- pc2
Traj_li[[3]] <- pc3
DLik <- c(2.1, 2.2, 4)

system.time({
BRBs_PC <- foreach(i = 1:length(Traj_li),
                      .combine = c,
                      .packages = c("adehabitatHR","adehabitatLT", "terra")) %dopar% {
                          BRB(Traj_li[[i]][1],
                          D = DLik[i],
                          Tmax = 1500*60,
                          Lmin = 2,
                          hmin = 20,
                          type = "UD",
                          grid = 4000)
                      }
    })

thenames <- c("pn1", "pn2", "pn3")
WRds <- function (x) {saveRDS(x,
                        paste0(here("BRB_UDs"), "/",
                        thenames,".Rds"))
                        }
BRBs_BM_WIv <- slurm_map(BRBs_BM_WI, f=WRds, nodes=2, cpus_per_node = 1)

报错信息

sbatch: error: Batch job submission failed: Requested partition configuration not available now

Error in strsplit(sys_out, " ")[[1]] : subscript out of bounds
In addition: Warning message:
In system("sbatch submit.sh", intern = TRUE) :
  running command 'sbatch submit.sh' had status 1

其他尝试的并行代码

# mclapply尝试
mclapply(X = BRBs_BM_WIv, FUN=WV, mc.cores = n.cores)

# parLapply尝试1
my.cluster <- parallel::makeCluster(
  n.cores, 
  type = "FORK"
  )
doParallel::registerDoParallel(cl = my.cluster)
WVec <- function (x) {vect(x)}
BRBs_BM_WIv <- parLapply(cl=my.cluster,
                         BRBs_BM_WI,
                         FUN = MVec)
stopCluster(my.cluster)

# parLapply尝试2
my.cluster <- parallel::makeCluster(
  n.cores, 
  type = "FORK"
  )
doParallel::registerDoParallel(cl = my.cluster)
WV <- function (x) {writeVector(x,
                            paste0(here("BRB_UDs"), "/",
                            thenames,".shp"),
                            overwrite=TRUE)}
parLapply(cl=my.cluster, X = BRBs_BM_WIv, FUN = MV)
stopCluster(my.cluster)

诉求

  1. 阐明高性能集群并行方法的内存管理逻辑(尤其是SLURM内存映射)
  2. 提供parLapply()、mclapply()或future::plan(multicore)的可行实现示例
  3. 明确共享内存与分布式内存的区别及在SLURM中的运行逻辑

解决方案

一、核心概念梳理

共享内存 vs 分布式内存

  • 共享内存:所有并行进程在同一节点上,共享同一物理内存空间。适合单节点多核并行(如mclapply、parallel::makeCluster(type="FORK")),进程可直接读取父进程的数据,无需序列化,但总内存受单节点限制。
  • 分布式内存:进程分布在多个节点,每个节点有独立内存空间。适合跨节点并行(如parallel::makeCluster(type="PSOCK")、slurm_map),进程间需通过网络传输数据,序列化开销大,但可利用集群多节点的总内存。

SLURM内存参数逻辑

  • --mem-per-cpu=<value>:为每个CPU核心分配指定内存,总内存=核心数×该值。
  • --mem=<value>:为整个任务分配固定内存(推荐使用,避免因核心数计算错误导致内存不足)。
  • 单节点任务:用--nodes=1 + --cpus-per-task=<n>,指定单节点用n个核心,内存用--mem设置单节点总可用内存。
  • 多节点任务:用--nodes=<m> + --ntasks-per-node=<k>,每个节点跑k个任务,总任务数=m×k。

二、可行并行实现示例

1. 单节点共享内存:mclapply(适合内存需求高、单节点能容纳的场景)

SLURM交互式命令

# 申请单节点,20核心,总内存240G(根据实际需求调整)
salloc --time=3:00:0 --nodes=1 --cpus-per-task=20 --mem=240G

R代码

library(adehabitatHR)
library(terra)
library(parallel)

# 加载数据(替换为你的UDEs列表)
data(puechcirc)
Traj_li <- list(puechcirc[1], puechcirc[2], puechcirc[3])
DLik <- c(2.1, 2.2, 4)

# 定义处理函数:计算BRB并转换为vect,直接保存结果(避免内存累积)
process_ude <- function(traj, d, name) {
  # 计算UDE
  ude <- BRB(traj[1],
             D = d,
             Tmax = 1500*60,
             Lmin = 2,
             hmin = 20,
             type = "UD",
             grid = 4000)
  # 转换为vect
  spatvec <- vect(ude)
  # 保存为Shapefile(直接写入磁盘,不返回大对象)
  writeVector(spatvec,
              paste0("BRB_UDs/", name, ".shp"),
              overwrite = TRUE)
  # 返回标识,确认完成
  return(paste(name, "processed"))
}

# 生成文件名列表
thenames <- paste0("pn", 1:length(Traj_li))

# 并行执行,mc.cores匹配SLURM申请的核心数
results <- mclapply(1:length(Traj_li),
                    function(i) {
                      process_ude(Traj_li[[i]], DLik[i], thenames[i])
                    },
                    mc.cores = 20)

# 查看结果
print(unlist(results))

2. 单节点共享内存:parLapply(FORK模式)

SLURM命令同mclapply示例

R代码

library(adehabitatHR)
library(terra)
library(parallel)

# 加载数据
data(puechcirc)
Traj_li <- list(puechcirc[1], puechcirc[2], puechcirc[3])
DLik <- c(2.1, 2.2, 4)
thenames <- paste0("pn", 1:length(Traj_li))

# 创建FORK集群(共享父进程内存,无需重新加载包/数据)
n_cores <- 20
my_cluster <- makeCluster(n_cores, type = "FORK")

# 定义处理函数
process_ude <- function(i) {
  ude <- BRB(Traj_li[[i]][1],
             D = DLik[i],
             Tmax = 1500*60,
             Lmin = 2,
             hmin = 20,
             type = "UD",
             grid = 4000)
  spatvec <- vect(ude)
  writeVector(spatvec,
              paste0("BRB_UDs/", thenames[i], ".shp"),
              overwrite = TRUE)
  return(paste(thenames[i], "processed"))
}

# 并行执行
results <- parLapply(my_cluster, 1:length(Traj_li), process_ude)

# 关闭集群
stopCluster(my_cluster)

print(unlist(results))

3. 多节点分布式内存:future + future.apply(适合超大规模任务)

SLURM交互式命令

# 申请2节点,每个节点10核心,总内存120G/节点
salloc --time=6:00:0 --nodes=2 --ntasks-per-node=10 --mem=120G

R代码

library(adehabitatHR)
library(terra)
library(future)
library(future.apply)

# 设置并行计划:multisession(跨节点PSOCK集群)
plan(multisession, workers = 20) # 2节点×10核心=20 workers

# 加载数据
data(puechcirc)
Traj_li <- list(puechcirc[1], puechcirc[2], puechcirc[3])
DLik <- c(2.1, 2.2, 4)
thenames <- paste0("pn", 1:length(Traj_li))

# 定义处理函数(需包含包加载,因为每个worker是独立进程)
process_ude <- function(i) {
  library(adehabitatHR)
  library(terra)
  
  ude <- BRB(Traj_li[[i]][1],
             D = DLik[i],
             Tmax = 1500*60,
             Lmin = 2,
             hmin = 20,
             type = "UD",
             grid = 4000)
  spatvec <- vect(ude)
  writeVector(spatvec,
              paste0("BRB_UDs/", thenames[i], ".shp"),
              overwrite = TRUE)
  return(paste(thenames[i], "processed"))
}

# 并行执行
results <- future_lapply(1:length(Traj_li), process_ude)

print(unlist(results))

三、内存优化关键技巧

  1. 避免内存累积:不要将所有UDEs/vect对象保存在内存中,处理一个就写入磁盘,只返回状态标识。
  2. 合理选择并行模式:单节点能容纳的任务优先用共享内存(FORK/mclapply),避免序列化开销;跨节点任务用分布式内存(PSOCK/future)。
  3. SLURM内存参数正确设置:优先用--mem指定单节点总内存,而非--mem-per-cpu,避免因核心数计算导致内存分配不足或浪费。
  4. 避免嵌套并行:adehabitatHR的部分函数可能自带OpenMP并行,需关闭或设置环境变量Sys.setenv(OMP_NUM_THREADS=1),避免与R的并行框架冲突。

内容的提问来源于stack exchange,提问作者Mark Thompson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 21:02:10