Skip to content

Commit

Permalink
Parallelize feature finding (ProteoWizard#3041)
Browse files Browse the repository at this point in the history
Improved Skyline's Hardklor/Bullseye-based feature finding by processing files in parallel.

Also made some performance improvements in Hardklor, Bullseye, and our own RT alignment code.
  • Loading branch information
bspratt authored Jul 29, 2024
1 parent 8e41351 commit 244eeeb
Show file tree
Hide file tree
Showing 17 changed files with 1,032 additions and 402 deletions.
5 changes: 4 additions & 1 deletion pwiz_tools/Shared/CommonUtil/SystemUtil/ProcessRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,10 @@ public void Run(ProcessStartInfo psi, string stdin, IProgressMonitor progress, r
{
if (progress.IsCanceled)
{
proc.Kill();
if (!proc.HasExited)
{
proc.Kill();
}
progress.UpdateProgress(status = status.Cancel());
CleanupTmpDir(psi); // Clean out any tempfiles left behind, if forceTempfilesCleanup was set
return;
Expand Down
2 changes: 1 addition & 1 deletion pwiz_tools/Skyline/Executables/BullseyeSharp
Submodule BullseyeSharp updated 2 files
+17 −6 BullseyeSharp.cs
+132 −92 CKronik2.cs
2 changes: 1 addition & 1 deletion pwiz_tools/Skyline/Executables/Hardklor/Hardklor
Submodule Hardklor updated 1 files
+14 −4 CHardklor2.cpp
51 changes: 1 addition & 50 deletions pwiz_tools/Skyline/FileUI/PeptideSearch/EncyclopeDiaSearchDlg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using pwiz.Common.Chemistry;
using pwiz.Common.Collections;
using pwiz.Common.Controls;
using pwiz.Common.SystemUtil;
using pwiz.Skyline.Alerts;
using pwiz.Skyline.Controls;
Expand Down Expand Up @@ -613,54 +611,7 @@ public EncyclopeDiaSearchControl(EncyclopeDiaSearchDlg hostControl)
public EncyclopeDiaSearchDlg.EncyclopeDiaSettings Settings { get; set; }
public string EncyclopeDiaChromLibraryPath { get; private set; }
public string EncyclopeDiaQuantLibraryPath { get; private set; }

public class ParallelRunnerProgressControl : MultiProgressControl, IProgressMonitor
{
private readonly EncyclopeDiaSearchControl _hostControl;

public ParallelRunnerProgressControl(EncyclopeDiaSearchControl hostControl)
{
_hostControl = hostControl;
ProgressSplit.Panel2Collapsed = true;
}

// ReSharper disable once InconsistentlySynchronizedField
public bool IsCanceled => _hostControl.IsCanceled;

public UpdateProgressResponse UpdateProgress(IProgressStatus status)
{
if (IsCanceled || status.IsCanceled)
return UpdateProgressResponse.cancel;

var match = Regex.Match(status.Message, @"(.*)\:\:(.*)");
Assume.IsTrue(match.Success && match.Groups.Count == 3,
@"ParallelRunnerProgressDlg requires a message like file::message to indicate which file's progress is being updated");

lock(this)
{
// only make the MultiProgressControl visible if it's actually used
if (RowCount == 0)
{
var hostDialog = _hostControl.HostDialog;
hostDialog.BeginInvoke(new MethodInvoker(() =>
{
_hostControl.progressSplitContainer.Panel1Collapsed = false;
hostDialog.Size = new Size(Math.Min(
Screen.FromControl(hostDialog).Bounds.Width * 90 / 100,
hostDialog.Width * 2), hostDialog.Height);
}));
}

string name = match.Groups[1].Value;
string message = match.Groups[2].Value;
Update(name, status.PercentComplete, message, status.ErrorException != null);
return IsCanceled ? UpdateProgressResponse.cancel : UpdateProgressResponse.normal;
}
}

public bool HasUI => true;
}


private bool Search(EncyclopeDiaSearchDlg.EncyclopeDiaSettings settings, CancellationTokenSource token, IProgressStatus status)
{
ParallelRunnerProgressControl multiProgressControl = null;
Expand Down
154 changes: 154 additions & 0 deletions pwiz_tools/Skyline/FileUI/PeptideSearch/HardklorSearchControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Original author: Brian Pratt <bspratt .at. proteinms dot net >
*
* Copyright 2024 University of Washington - Seattle, WA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Threading;
using System.Windows.Forms;
using pwiz.Common.SystemUtil;
using pwiz.Skyline.Model;
using pwiz.Skyline.Properties;
using pwiz.Skyline.Util.Extensions;
using pwiz.Skyline.Model.DdaSearch;

namespace pwiz.Skyline.FileUI.PeptideSearch
{
public class HardklorSearchControl : SearchControl
{
private ImportPeptideSearch ImportPeptideSearch;
private ImportPeptideSearch.HardklorSettings _settings;
private int _totalSteps;
private int _currentStep;
HardklorSearchEngine FeatureFinder => ImportPeptideSearch.SearchEngine as HardklorSearchEngine;


public HardklorSearchControl(ImportPeptideSearch importPeptideSearch)
{
ImportPeptideSearch = importPeptideSearch;
}

// Steps, in parallel, for n files:
// Threads 0 through (n-1): per file, convert to mzML, then do HardKlor then Bullseye
// Thread n: Perform alignments on all mzMLs as they become available
// Finally combine the results across replicates

private bool Search(ImportPeptideSearch.HardklorSettings settings, CancellationTokenSource token)
{
_settings = settings;
ParallelRunnerProgressControl multiProgressControl = null;
try
{
Invoke(new MethodInvoker(() =>
{
multiProgressControl = new ParallelRunnerProgressControl(this);
multiProgressControl.Dock = DockStyle.Fill;
progressSplitContainer.Panel1.Controls.Add(multiProgressControl);
}));

FeatureFinder.Generate(multiProgressControl, this, token.Token);
}
catch (OperationCanceledException e)
{
UpdateProgress(_status.ChangeWarningMessage(e.InnerException?.Message ?? e.Message));
return false;
}
catch (Exception e)
{
UpdateProgress(_status.ChangeErrorException(e));
return false;
}
finally
{
Invoke(new MethodInvoker(() =>
{
progressSplitContainer.Panel1Collapsed = true;
progressSplitContainer.Panel1.Controls.Clear();
multiProgressControl?.Dispose();
}));
}

return !token.IsCancellationRequested;
}

public override UpdateProgressResponse UpdateProgress(IProgressStatus status)
{
if (IsCanceled)
{
return UpdateProgressResponse.cancel;
}

lock (this)
{
_status = _status.ChangeMessage(status.Message).ChangePercentComplete((100 * _currentStep++) / _totalSteps);
}

BeginInvoke(new MethodInvoker(() => UpdateSearchEngineProgress(_status)));

return UpdateProgressResponse.normal;
}




public override void RunSearch()
{
// ImportPeptideSearch.SearchEngine.SearchProgressChanged += SearchEngine_MessageNotificationEvent;
txtSearchProgress.Text = string.Empty;
_progressTextItems.Clear();
btnCancel.Enabled = progressBar.Visible = true;

_cancelToken = new CancellationTokenSource();
FeatureFinder.SetCancelToken(_cancelToken);

ActionUtil.RunAsync(RunSearchAsync, @"Feature Finding Search thread");
}

private IProgressStatus _status;
private void RunSearchAsync()
{
_totalSteps = (ImportPeptideSearch.SearchEngine.SpectrumFileNames.Length * 4) + 2; // Per-file: msconvert, Hardklor, Bullseye, RTAlign prep. All-files: RT alignment, combine features
_currentStep = 0;

_status = new ProgressStatus();

bool success = true;

if (!_cancelToken.IsCancellationRequested)
{
UpdateProgress(_status = _status.ChangeMessage(PeptideSearchResources.DDASearchControl_SearchProgress_Starting_search));

success = Search(_settings, _cancelToken);

Invoke(new MethodInvoker(() => UpdateSearchEngineProgressMilestone(_status, success, _status.SegmentCount,
Resources.DDASearchControl_SearchProgress_Search_canceled,
PeptideSearchResources.DDASearchControl_SearchProgress_Search_failed,
Resources.DDASearchControl_SearchProgress_Search_done)));
}


Invoke(new MethodInvoker(() =>
{
UpdateTaskbarProgress(TaskbarProgress.TaskbarStates.NoProgress, 0);
btnCancel.Enabled = false;
OnSearchFinished(success);
// ImportPeptideSearch.SearchEngine.SearchProgressChanged -= SearchEngine_MessageNotificationEvent;
}));
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,14 @@ public ImportPeptideSearchDlg(SkylineWindow skylineWindow, LibraryManager librar
AddPageControl(SearchSettingsControl, ddaSearchSettingsPage, 18, isFeatureDetection ? this.buildSpectralLibraryTitlePanel.Bottom : 50);
}

SearchControl = new DDASearchControl(ImportPeptideSearch);
if (isFeatureDetection)
{
SearchControl = new HardklorSearchControl(ImportPeptideSearch);
}
else
{
SearchControl = new DDASearchControl(ImportPeptideSearch);
}
AddPageControl(SearchControl, ddaSearchPage, isFeatureDetection ? 3 : 18, 50);
if (isFeatureDetection)
{
Expand Down Expand Up @@ -453,7 +460,7 @@ public void AdjustHeightForFullScanSettings()
public MatchModificationsControl MatchModificationsControl { get; private set; }
public ConverterSettingsControl ConverterSettingsControl { get; private set; }
public SearchSettingsControl SearchSettingsControl { get; private set; }
public DDASearchControl SearchControl { get; private set; }
public SearchControl SearchControl { get; private set; }

public ImportResultsControl ImportResultsDDAControl { get; private set; }
public ImportResultsDIAControl ImportResultsDIAControl { get; private set; }
Expand Down
58 changes: 57 additions & 1 deletion pwiz_tools/Skyline/FileUI/PeptideSearch/SearchControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using pwiz.Common.Collections;
using pwiz.Common.Controls;
using pwiz.Common.SystemUtil;
using pwiz.Skyline.Alerts;
using pwiz.Skyline.Util;

namespace pwiz.Skyline.FileUI.PeptideSearch
{
Expand Down Expand Up @@ -56,6 +59,12 @@ public string ToString(bool showTimestamp)
return showTimestamp ? $"[{Timestamp.ToString("yyyy/MM/dd HH:mm:ss")}] {Message}" : Message;
// ReSharper restore LocalizableElement
}

public override string ToString()
{
return ToString(true); // For debugging convenience
}

}

protected List<ProgressEntry> _progressTextItems = new List<ProgressEntry>();
Expand Down Expand Up @@ -230,7 +239,7 @@ private void RefreshProgressTextbox()
public bool IsCanceled => _cancelToken.IsCancellationRequested;

/// progress updates from AbstractDdaConverter (should be prefixed by the file currently being processed)
public UpdateProgressResponse UpdateProgress(IProgressStatus status)
public virtual UpdateProgressResponse UpdateProgress(IProgressStatus status)
{
if (IsCanceled)
return UpdateProgressResponse.cancel;
Expand All @@ -242,5 +251,52 @@ public UpdateProgressResponse UpdateProgress(IProgressStatus status)

return UpdateProgressResponse.normal;
}

public class ParallelRunnerProgressControl : MultiProgressControl, IProgressMonitor
{
private readonly SearchControl _hostControl;

public ParallelRunnerProgressControl(SearchControl hostControl)
{
_hostControl = hostControl;
ProgressSplit.Panel2Collapsed = true;
}

// ReSharper disable once InconsistentlySynchronizedField
public bool IsCanceled => _hostControl.IsCanceled;

public UpdateProgressResponse UpdateProgress(IProgressStatus status)
{
if (IsCanceled || status.IsCanceled)
return UpdateProgressResponse.cancel;

var match = Regex.Match(status.Message, @"(.*)\:\:(.*)");
Assume.IsTrue(match.Success && match.Groups.Count == 3,
@"ParallelRunnerProgressDlg requires a message like file::message to indicate which file's progress is being updated");

lock (this)
{
// only make the MultiProgressControl visible if it's actually used
if (RowCount == 0)
{
var hostDialog = _hostControl.Parent;
hostDialog.BeginInvoke(new MethodInvoker(() =>
{
_hostControl.progressSplitContainer.Panel1Collapsed = false;
hostDialog.Size = new Size(Math.Min(
Screen.FromControl(hostDialog).Bounds.Width * 90 / 100,
hostDialog.Width * 2), hostDialog.Height);
}));
}

string name = match.Groups[1].Value;
string message = match.Groups[2].Value;
Update(name, status.PercentComplete, message, status.ErrorException != null);
return IsCanceled ? UpdateProgressResponse.cancel : UpdateProgressResponse.normal;
}
}

public bool HasUI => true;
}
}
}
Loading

0 comments on commit 244eeeb

Please sign in to comment.