使用队列系统
队列系统的作用
简而言之:排队。你告诉队列系统使用什么资源(例如多少核的 CPU)运行某个程序,轮到你的任务执行时,队列系统会按照你的要求执行任务。 队列系统会考虑服务器的负载能力(不可以同时运行太多任务把服务器挤垮)、有多人使用服务器时公平分配资源,以及记录任务执行过程中的开销。
队列系统不与某个特定的计算软件(例如 VASP)绑定。 也就是说,你可以脱离队列系统直接运行 VASP,也可以把别的软件也放到队列系统里运行;它们虽然经常在一起使用,但本质上是两个分离的概念。1
使用队列系统
不同的服务器使用不同的队列系统。
- 厦大超算(jykang):使用 LSF。
- srv1:使用 Slurm。
- srv1(Windows):没有队列系统,但请阅读 Windows 章节,了解相关注意事项。
- xmupc1 和 xmupc2:也使用 Slurm,但有一些不同,见 Slurm(旧)。
Windows
Windows 没有通用的免费的队列系统程序,通常手动控制任务依次执行。
我曾经在2024年暑假答应鹏岗帮他写一个,但事实是我把他鸽了,有空一定写,咕咕咕。
你可以使用 FDTD Solutions 自带的一个单用户的队列系统。
在 FDTD Solutions 内的控制台使用 addjob
命令将要模拟的文件加入队列,然后它就会依次运行。
文档见这里。
例如,如果一个任务需要一个小时跑完,那么按顺序运行两个任务需要两个小时,但是同时运行两个任务需要的时间往往远大于两个小时。 若同时运行 10 个任务,可能连系统都没有反应了。
一个简易的队列系统的实现
下面是我做 FDTD 计算时写的一个小玩意儿,
通过判断当前目录(包括子目录)下面的各个 fsp 是否存在对应的 xxx_p0.log
文件来判断每个 fsp 是否已经被模拟过,如果没有就依次模拟。
每一次模拟完成后都会再扫描一遍整个目录路,因此可以动态地把需要模拟的 fsp 在多台服务器之间转移(负载均衡)。
代码兼容 Linux 和 Windows,模拟完一个 fsp 之后还会给我发个消息(这样我可以知道进度)。
你可以根据自己的需求修改使用,或者用 Python 之类你更熟悉的语言重写。
# define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <filesystem>
#include <iostream>
#include <cstdlib>
#include <regex>
#include <string>
#include <thread>
#include <set>
#include <fstream>
# define CPPHTTPLIB_OPENSSL_SUPPORT
#include <httplib.h>
using namespace std::literals;
int main()
{
while (true)
{
std::set<std::string> fsp, log;
std::string job;
// std::string hostname;
for (const auto& entry : std::filesystem::recursive_directory_iterator("."))
{
std::string file = entry.path().string();
if (std::regex_match(file, std::regex(R"(^(.*)\.fsp$)")))
fsp.insert(file);
else if (std::regex_match(file, std::regex(R"(^(.*)_p0\.log$)")))
log.insert(file);
}
for (const auto& f : fsp)
if (!log.contains(std::regex_replace(f, std::regex(R"(^(.*)\.fsp$)"), "$1_p0.log")))
{
job = f;
break;
}
if (job == "")
break;
// {
// std::ifstream hostname_ifs("hostname.txt");
// hostname_ifs >> hostname;
// }
auto current_path = std::filesystem::current_path();
std::filesystem::current_path(std::filesystem::path(job).parent_path());
auto filename = std::filesystem::path(job).filename().string();
// if windows
# ifdef _WIN32
std::string command = "fdtd-solutions -run run.lsf -exit -trust-script";
# else
std::string command = "DISPLAY=:0 /opt/lumerical/fdtd/bin/fdtd-solutions -nw -run run.lsf -exit";
# endif
std::cout << fmt::format
(
"\r{}/{} running {} from {}\n command {}",
log.size(),
fsp.size(),
filename,
std::filesystem::current_path().string(),
command
)
<< std::flush;
// {
// // https://api.chn.moe/notify_silent.php?message={hostname} {}/{}
// httplib::SSLClient client("api.chn.moe");
// std::string path = fmt::format("/notify_silent.php?message={}%20{}%2F{}", hostname, log.size(), fsp.size());
// client.Get(path.c_str());
// }
{
std::ofstream os("run.lsf");
os << fmt::format(R"(
load("{}");
switchtolayout;
run;
)", filename);
os.close();
}
system(command.c_str());
std::this_thread::sleep_for(5s);
std::filesystem::remove("run.lsf");
std::filesystem::current_path(current_path);
}
}
Slurm(旧)
与 Slurm(新)基本相同,只是 VASP(CPU)的打包方式不同,使得提交任务的命令略有不同。 下一次服务器维护时,xmupc1 和 xmupc2 计划合并为一个集群,并且应用新的打包方式。
提交 VASP(CPU)任务的例子:
sbatch --ntasks=2 --cpus-per-task=4 --hint=nomultithread --job-name="my great job" --output=output.txt vasp-intel-std
-
实际上队列系统与要运行的软件还是有许多耦合的,尤其是使用 MPI 并行的程序(包括绝大多数成熟的大型科学计算软件)。 如何让队列系统与这些软件对接好有时会很麻烦,只是普通用户不需要关心(我替你们搞定了)。 ↩︎