今天整点.Net。因为在某个业余小项目中,需要尽可能榨干硬件性能,以满足尽可能大的可访问数据范围。
发现在.Net中并行计算一般使用System.Threading.Task.Parallel,但实际开发中,似乎只能处理到1G左右的数据就会溢出,机器整个状态似乎只是在摸鱼。并且拥有多台物理机器的时候并不能共享计算资源。
第一步,将多线程的程序改为多进程+多线程。输入是一个文件路径列表,是一堆pdf。需要使用OCR识别pdf内嵌图片中的内容,输入大模型完成校准,输出校准后的txt,后续成为构建向量数据库用的基础数据。
多线程好办,直接用Parallel干就好了。多进程多少有些麻烦。主要困难的点在于进程间数据共享。解决这种困难第一时间去找开源库,然鹅并没有这种的。因为写python的时候joblib里面Parallel一下,delayed一个表达式搞定的事情,在.Net并没有类似的东西。随即写了一个。叫做MPILibrary。实际上真的要做MPI很复杂,有专门一个学科研究这东西,这里姑且用这个名字了。
发布在nuget了,MIT License,直接可以搜来使用,自用商用都行。
NuGet Gallery | MPILibrary 1.0.4
源码开放在Github
lu1770/MPILibrary (github.com)
基本做法比较简单,第一步,开一个可执行程序,在Main函数开头加上子进程处理逻辑:
if (ProcessParallel.IsSubProcess()) { ProcessParallel.Handle(); return; }
第二步,定义一个静态方法,本来想做成支持lambda表达式失败了,先用静态方法好了。
class Runner { public static object Run(string id) { return new { CommandLineArgs = Environment.GetCommandLineArgs(), Message = $"input is {id}, process id is {Process.GetCurrentProcess().Id}", Time=DateTime.Now.ToShortTimeString() }; } }
第三步,传入待处理数据,启动执行。
// Generate Items var items = Enumerable.Range(1, 200).Select(i => Guid.NewGuid().ToString()); // Show MaxProcessLimit Console.WriteLine($"Start {ProcessParallel.MaxProcessLimit}"); // Run List<object> results = ProcessParallel.ForEach(items, Runner.Run).ToList(); // Show Results Console.WriteLine(JsonConvert.SerializeObject(results, Formatting.Indented));
这里items中每个元素会开一个专门的进程执行Run,这样单进程内存上限差不多是1.2G。
处理完成之后,从Run返回的结果会作为元素存放在results里面,这里出来的结果是乱序的,对顺序敏感的话,可以设计一个特别的keypair的返回结构自行实现完成后排序。
MaxProcessLimit这个用于控制进程数的,默认使用当前机器CPU内核数,防止实际编程忽略此设置,实现又过于奔放,把机器玩没了的情况。当然,如果实际items特别少,机器性能又超强的情况下使用int.MaxValue也未尝不可。实际测试下来默认值效果最好,默认值1.5倍会有鼠标键盘操作无响应的情况,酌情使用把。
进程间通信使用JSON格式的,所以输入输出的格式需要可以被JSON序列化的结构。
未来版本,考虑使用以太网实现多物理机的IPC,这样多台机器组合并行,通过修改一个配置项,获得相当可观的计算能力。
老板们有需要直接提issue把,有用的会抽空去做。
标签:ProcessParallel,Run,items,Library,MPI,进程,Net,多线程 From: https://www.cnblogs.com/lu1770/p/18000473