-
Notifications
You must be signed in to change notification settings - Fork 121
Improve performance of saving .sky file #4136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 5 commits
972a842
2c5195f
3855a45
5138ee2
3ec3fc9
9c67cbb
a110d60
5def88c
d5e011a
65d4690
604c9bd
95f4b56
5dd2d98
02db777
278d568
fd69a69
8523abd
33f370d
66767b4
7c7ffdc
c07675b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,8 +17,10 @@ | |
| * limitations under the License. | ||
| */ | ||
| using System; | ||
| using System.Collections.Concurrent; | ||
| using System.IO; | ||
| using System.Security.Cryptography; | ||
| using System.Threading; | ||
|
|
||
| namespace pwiz.Skyline.Model.AuditLog | ||
| { | ||
|
|
@@ -113,13 +115,19 @@ public class HashingStream : Stream | |
| private readonly SHA1CryptoServiceProvider _sha1; | ||
| private readonly BlockHash _blockHash; | ||
| private readonly bool _keepOpen; | ||
| private readonly BlockingCollection<byte[]> _hashQueue; | ||
| private readonly Thread _hashThread; | ||
| private Exception _hashException; | ||
|
|
||
| public HashingStream(Stream inner, bool keepOpen) | ||
| { | ||
| _inner = inner; | ||
| _keepOpen = keepOpen; | ||
| _sha1 = new SHA1CryptoServiceProvider(); | ||
| _blockHash = new BlockHash(_sha1); | ||
| _hashQueue = new BlockingCollection<byte[]>(); | ||
| _hashThread = new Thread(HashWorker) { IsBackground = true }; | ||
| _hashThread.Start(); | ||
| } | ||
|
|
||
| public static Stream CreateWriteStream(string path) | ||
|
|
@@ -139,7 +147,10 @@ public override int Read(byte[] buffer, int offset, int count) | |
| var bytesRead = _inner.Read(buffer, offset, count); | ||
| if (bytesRead <= 0) | ||
| return bytesRead; | ||
| _blockHash.ProcessBytes(buffer, bytesRead); | ||
|
|
||
| var copy = new byte[bytesRead]; | ||
| Array.Copy(buffer, offset, copy, 0, bytesRead); | ||
| _hashQueue.Add(copy); | ||
|
|
||
| return bytesRead; | ||
| } | ||
|
|
@@ -148,24 +159,31 @@ public override void Write(byte[] buffer, int offset, int count) | |
| { | ||
| _inner.Write(buffer, offset, count); | ||
|
|
||
| _blockHash.ProcessBytes(buffer, count); | ||
| var copy = new byte[count]; | ||
| Array.Copy(buffer, offset, copy, 0, count); | ||
| _hashQueue.Add(copy); | ||
| } | ||
|
Comment on lines
+145
to
165
|
||
|
|
||
|
|
||
| public string Hash | ||
| public string GetHash() | ||
| { | ||
| get { return BlockHash.SafeToBase64(HashBytes); } | ||
| return BlockHash.SafeToBase64(GetHashBytes()); | ||
| } | ||
|
|
||
| public byte[] HashBytes | ||
| public byte[] GetHashBytes() | ||
| { | ||
| get { return _blockHash.HashBytes; } | ||
| WaitForHashThread(); | ||
| return _blockHash.HashBytes; | ||
| } | ||
|
Comment on lines
+168
to
177
|
||
|
|
||
| public string Done() | ||
| { | ||
| _hashQueue.CompleteAdding(); | ||
| _hashThread.Join(); | ||
| if (_hashException != null) | ||
| throw _hashException; | ||
| _blockHash.FinalizeHashBytes(); | ||
| return Hash; | ||
| return GetHash(); | ||
| } | ||
|
|
||
| protected override void Dispose(bool disposing) | ||
|
|
@@ -174,6 +192,10 @@ protected override void Dispose(bool disposing) | |
|
|
||
| if (disposing) | ||
| { | ||
| if (!_hashQueue.IsAddingCompleted) | ||
| _hashQueue.CompleteAdding(); | ||
| _hashThread.Join(); | ||
| _hashQueue.Dispose(); | ||
| if (!_keepOpen) | ||
| { | ||
| _inner.Dispose(); | ||
|
|
@@ -182,6 +204,29 @@ protected override void Dispose(bool disposing) | |
| } | ||
| } | ||
|
|
||
| private void HashWorker() | ||
| { | ||
| try | ||
| { | ||
| foreach (var bytes in _hashQueue.GetConsumingEnumerable()) | ||
| { | ||
| _blockHash.ProcessBytes(bytes); | ||
| } | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| _hashException = ex; | ||
| } | ||
| } | ||
|
|
||
| private void WaitForHashThread() | ||
| { | ||
| if (_hashThread.IsAlive) | ||
| _hashThread.Join(); | ||
| if (_hashException != null) | ||
| throw _hashException; | ||
| } | ||
|
|
||
| #region Unused wrappers | ||
| public override void Flush() | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_hashQueue is unbounded, and Write/Read enqueue copies faster than the hashing thread can consume them. For large files this can grow without limit and spike memory usage. Consider using a bounded BlockingCollection capacity and applying backpressure (block the writer when the queue is full).