引子
大模型的训练和推理是很耗时的,如果没有及时知晓训练/推理完成的时刻,则可能会造成时间上的损失。比如睡前明明可以再跑一个模型,结果认为上一个模型没跑完,就浪费了一晚上的时间。代码开源在https://github.com/coder109/Email-After-Process-Done。
所以,我做了这样一个工具,监控进程运行的开始、结束时刻,结束了就把信息发给我的邮箱。大概的设计是这样的:记录时间-运行进程-记录时间-发送邮件。
主函数
主函数也是按照设计来的:
func main() {
// Init
var info pkg.Information
pkg.GetTime(&info, "start")
email_info := pkg.InitPrivateEmailInformation()
// Get the Command Name
command := pkg.BuildCommand(os.Args)
pkg.GetCommandName(&info)
// Find the Path
path := pkg.LookPath(command)
// Start the Process
pro, err := os.StartProcess(path, command, &os.ProcAttr{})
if err != nil {
panic(err)
}
// Wait
pro.Wait()
pkg.GetTime(&info, "end")
content := pkg.InformationToString(info)
fmt.Println(content)
pkg.SendEmail(content, email_info.Sender, email_info.Recver, email_info.Port, email_info.Smtp_server, email_info.Password)
}
- 维护一个信息,便于最后发送邮件。
- 随后获取当前的时间,也就是进程开始运行的时间。
- 记录命令,并且将命令也记录到信息中。
- 记录可执行程序的地址,便于
StartProcess函数使用。 - 如果程序完美无缺地执行了,就等待程序运行结束,记录结束时间并发送邮件。
- 否则,就报错。
这里存在着可以优化的一点,就是如果出错了也发邮件,但是,这一个更新我想放在后面,将运行结果、日志或者错误作为附件。
command的维护
command的维护主要集中在代码中的executor.go文件中,其主要内容如下:
func BuildCommand(args []string) []string {
if len(args) == 1 {
panic("No Command")
}
command := append([]string{}, args[1:]...)
return command
}
func LookPath(command_name []string) string {
path, err := exec.LookPath(command_name[0])
if err != nil {
panic(err)
}
return path
}这里的内容很简单,没什么可说的。
邮件发送
邮件发送的代码在代码的sender.go中,主要应用了外部的email库。
信息维护
信息维护主要集中在代码的timelogger.go中,代码如下:
type Information struct {
command_name []string
time_start [6]string
time_end [6]string
}
func Month2Num(month string) int {
return map[string]int{
"January": 1,
"February": 2,
"March": 3,
"April": 4,
"May": 5,
"June": 6,
"July": 7,
"August": 8,
"September": 9,
"October": 10,
"November": 11,
"December": 12,
}[month]
}
func GetCommandName(information *Information) {
information.command_name = os.Args[1:]
}
func GetTime(information *Information, flag string) {
now := time.Now()
year, raw_month, day := now.Date()
hour, minute, second := now.Clock()
month := Month2Num(raw_month.String())
if flag == "start" {
information.time_start = [6]string{fmt.Sprint(year), fmt.Sprint(month), fmt.Sprint(day), fmt.Sprint(hour), fmt.Sprint(minute), fmt.Sprint(second)}
} else if flag == "end" {
information.time_end = [6]string{fmt.Sprint(year), fmt.Sprint(month), fmt.Sprint(day), fmt.Sprint(hour), fmt.Sprint(minute), fmt.Sprint(second)}
} else {
panic("Invalid Flag")
}
}
func InformationToString(information Information) string {
return fmt.Sprintf(
"The command name is %v\nThe start time is %s-%s-%s %s:%s:%s\nThe end time is %s-%s-%s %s:%s:%s\n",
information.command_name,
information.time_start[0], information.time_start[1], information.time_start[2], information.time_start[3], information.time_start[4], information.time_start[5],
information.time_end[0], information.time_end[1], information.time_end[2], information.time_end[3], information.time_end[4], information.time_end[5],
)
}
主要就是对时间的处理,以及最后的信息输出。
总结
这个代码很简单,但是也能体现出golang相比于C的优势——表达力更强,可应用的库多且易写。