我在代码中添加了多线程部分。
public class ThreadClassSeqGroups
{
public Dictionary<string, string> seqGroup;
public Dictionary<string, List<SearchAlgorithm.CandidateStr>> completeModels;
public Dictionary<string, List<SearchAlgorithm.CandidateStr>> partialModels;
private Thread nativeThread;
public ThreadClassSeqGroups(Dictionary<string, string> seqs)
{
seqGroup = seqs;
completeModels = new Dictionary<string, List<SearchAlgorithm.CandidateStr>>();
partialModels = new Dictionary<string, List<SearchAlgorithm.CandidateStr>>();
}
public void Run(DescrStrDetail dsd, DescrStrDetail.SortUnit primarySeedSu,
List<ushort> secondarySeedOrder, double partialCutoff)
{
nativeThread = new Thread(() => this._run(dsd, primarySeedSu, secondarySeedOrder, partialCutoff));
nativeThread.Priority = ThreadPriority.Highest;
nativeThread.Start();
}
public void _run(DescrStrDetail dsd, DescrStrDetail.SortUnit primarySeedSu,
List<ushort> secondarySeedOrder, double partialCutoff)
{
int groupSize = this.seqGroup.Count;
int seqCount = 0;
foreach (KeyValuePair<string, string> p in seqGroup)
{
Console.WriteLine("ThreadID {0} (priority:{1}):t#{2}/{3} SeqName: {4}",
nativeThread.ManagedThreadId, nativeThread.Priority.ToString(), ++seqCount, groupSize, p.Key);
List<SearchAlgorithm.CandidateStr> tmpCompleteModels, tmpPartialModels;
SearchAlgorithm.SearchInBothDirections(
p.Value.ToUpper().Replace('T', 'U'), dsd, primarySeedSu, secondarySeedOrder, partialCutoff,
out tmpCompleteModels, out tmpPartialModels);
completeModels.Add(p.Key, tmpCompleteModels);
partialModels.Add(p.Key, tmpPartialModels);
}
}
public void Join()
{
nativeThread.Join();
}
}
class Program
{
public static int _paramSeqGroupSize = 2000;
static void Main(Dictionary<string, string> rawSeqs)
{
// Split the whole rawSeqs (Dict<name, seq>) into several groups
Dictionary<string, string>[] rawSeqGroups = SplitSeqFasta(rawSeqs, _paramSeqGroupSize);
// Create a thread for each seqGroup and run
var threadSeqGroups = new MultiThreading.ThreadClassSeqGroups[rawSeqGroups.Length];
for (int i = 0; i < rawSeqGroups.Length; i++)
{
threadSeqGroups[i] = new MultiThreading.ThreadClassSeqGroups(rawSeqGroups[i]);
//threadSeqGroups[i].SetPriority();
threadSeqGroups[i].Run(dsd, primarySeedSu, secondarySeedOrder, _paramPartialCutoff);
}
// Merge results from threads after the thread finish
var allCompleteModels = new Dictionary<string, List<SearchAlgorithm.CandidateStr>>();
var allPartialModels = new Dictionary<string, List<SearchAlgorithm.CandidateStr>>();
foreach (MultiThreading.ThreadClassSeqGroups t in threadSeqGroups)
{
t.Join();
foreach (string name in t.completeModels.Keys)
{
allCompleteModels.Add(name, t.completeModels[name]);
}
foreach (string name in t.partialModels.Keys)
{
allPartialModels.Add(name, t.partialModels[name]);
}
}
}
}
但是,多线程的速度比单线程慢得多,CPU负载一般为<10%。
例如:
输入文件包含 2500 个字符串
_paramGroupSize = 3000,主线程 + 1 计算线程成本 200 秒
_paramGroupSize = 400,主线程 + 7 个计算线程花费更多时间(我在运行超过 10 分钟后杀死了它(。
我的实现有任何问题吗?如何加快速度?
谢谢。
在我看来,您正在尝试与多个线程并行处理文件。这是一个坏主意,假设您有一个机械磁盘。
基本上,磁盘头需要为每个读取请求寻找下一个读取位置。这是一个昂贵的操作,并且由于多个线程发出读取命令,这意味着当每个线程轮到运行时,头部会被反弹。与单个线程执行读取的情况相比,这将大大降低性能。
多线程之前的代码是什么? 很难说这段代码在做什么,而且许多"工作"代码似乎隐藏在你的搜索算法中。 但是,一些想法:
- 您提到了"输入文件",但这在代码中没有清楚地显示 - 如果您的文件访问是线程化的,这不会提高性能,因为文件访问将成为瓶颈。
- 创建比 CPU 内核多的线程最终会降低性能(除非每个线程在等待不同资源时被阻止(。 在您的情况下,我建议总共 8 个线程太多了。
- 似乎很多数据(内存(访问可以通过类
DescrStrDetail
完成,该类从Main
方法中的变量dsd
传递到每个子线程。 但是,缺少此变量的声明,因此其用法/实现未知。 如果此变量具有阻止多个线程同时访问的锁,则多个线程可能会将彼此锁定在此数据之外,从而进一步降低性能。
当线程运行时,它们在特定处理器上被赋予时间。 如果线程数多于处理器数,则系统上下文将在线程之间切换,以使所有活动线程有时间进行处理。 上下文切换非常昂贵。 如果线程数多于处理器数,则上下文切换会占用大部分 CPU 时间,并使单线程解决方案看起来比多线程解决方案更快。
您的示例显示启动不确定数量的线程。 如果SplitSeqFasta
返回的条目多于内核,您将创建更多的线程和内核,并引入大量的上下文切换。
我建议您手动限制线程数,或使用线程并行库和并行类之类的东西来自动限制它。