Long-running Process with Responsive Form - Performance Improvements
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
' ** FIRST ATTEMPT
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** FIRST ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 2:12.07
' ** Lines Written: 65358
' ** File Size: 8,166 KB
' ** SECOND ATTEMPT
stopWatch.Start()
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
TestOutputFile.Write(.ReadToEndAsync.Result)
End Using
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** SECOND ATTEMPT
' ** Records Retrieved: 65358
' ** Time To Complete: 1:04.01
' ** Lines Written: 65358
' ** File Size: 8,102 KB
End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress
method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code. The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async
method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
' ** METHOD 3
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
End Using
' ** METHOD 3 RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:15.07
' ** Lines Written: 34
' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker
, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress
method which caused the application to completely skip the actual dumping process. I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions. This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm
class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
Public Class FormHandler
Implements IDisposable
Public Property ApplicationForm As Form
Public Property ApplicationStatusLabel As Label
Public Property ApplicationToolStripLabel As ToolStripStatusLabel
Public Property ApplicationProgressBar As ProgressBar
Private LabelVisibleState As Boolean = True
Private ProgressBarVisibleState As Boolean = True
Private CurrentStatusText As String
Private CurrentProgress As Integer
Public Sub New(ByVal AppForm As Form)
ApplicationForm = AppForm
End Sub
Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String)
CurrentStatusText = StatusText
CurrentProgress = 0
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub ResetProgressUpdate()
CurrentStatusText = ""
CurrentProgress = 0
UpdateStatus()
End Sub
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress(CurrentStatusText)
End If
End If
End Sub
Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not ProgressText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Text = ProgressText
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Text = ProgressText
End If
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Value = CurrentProgress
End If
End With
ApplicationForm.Refresh()
Application.DoEvents()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Not ApplicationForm Is Nothing Then
ApplicationForm.Dispose()
End If
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Visible = LabelVisibleState
ApplicationStatusLabel.Dispose()
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Visible = LabelVisibleState
ApplicationToolStripLabel.Dispose()
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Visible = ProgressBarVisibleState
ApplicationProgressBar.Dispose()
End If
End Sub
End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress
variable as an Integer
), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
Dim CurrentProgress As Integer = 0
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
' ** Checks to see if the value of the ProgressBar will actually
' ** be changed by the CurrentPosition before making a call to
' ** UpdateProgress. If the value doesn't change, don't waste
' ** the call
If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(CurrentProgress)
End If
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress
method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements. Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress()
End If
End If
End Sub
Private Sub UpdateApplicationProgress()
Dim Changed As Boolean = False
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not CurrentStatusText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
If ApplicationStatusLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationStatusLabel.Text = CurrentStatusText
End If
End If
If Not ApplicationToolStripLabel Is Nothing Then
If ApplicationToolStripLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationToolStripLabel.Text = CurrentStatusText
End If
End If
End If
If Not ApplicationProgressBar Is Nothing Then
If ApplicationProgressBar.Value <> CurrentProgress Then
Changed = True
ApplicationProgressBar.Value = CurrentProgress
End If
End If
End With
If Changed Then
ApplicationForm.Refresh()
End If
Application.DoEvents()
End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
add a comment |
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
' ** FIRST ATTEMPT
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** FIRST ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 2:12.07
' ** Lines Written: 65358
' ** File Size: 8,166 KB
' ** SECOND ATTEMPT
stopWatch.Start()
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
TestOutputFile.Write(.ReadToEndAsync.Result)
End Using
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** SECOND ATTEMPT
' ** Records Retrieved: 65358
' ** Time To Complete: 1:04.01
' ** Lines Written: 65358
' ** File Size: 8,102 KB
End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress
method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code. The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async
method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
' ** METHOD 3
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
End Using
' ** METHOD 3 RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:15.07
' ** Lines Written: 34
' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker
, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress
method which caused the application to completely skip the actual dumping process. I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions. This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm
class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
Public Class FormHandler
Implements IDisposable
Public Property ApplicationForm As Form
Public Property ApplicationStatusLabel As Label
Public Property ApplicationToolStripLabel As ToolStripStatusLabel
Public Property ApplicationProgressBar As ProgressBar
Private LabelVisibleState As Boolean = True
Private ProgressBarVisibleState As Boolean = True
Private CurrentStatusText As String
Private CurrentProgress As Integer
Public Sub New(ByVal AppForm As Form)
ApplicationForm = AppForm
End Sub
Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String)
CurrentStatusText = StatusText
CurrentProgress = 0
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub ResetProgressUpdate()
CurrentStatusText = ""
CurrentProgress = 0
UpdateStatus()
End Sub
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress(CurrentStatusText)
End If
End If
End Sub
Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not ProgressText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Text = ProgressText
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Text = ProgressText
End If
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Value = CurrentProgress
End If
End With
ApplicationForm.Refresh()
Application.DoEvents()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Not ApplicationForm Is Nothing Then
ApplicationForm.Dispose()
End If
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Visible = LabelVisibleState
ApplicationStatusLabel.Dispose()
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Visible = LabelVisibleState
ApplicationToolStripLabel.Dispose()
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Visible = ProgressBarVisibleState
ApplicationProgressBar.Dispose()
End If
End Sub
End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress
variable as an Integer
), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
Dim CurrentProgress As Integer = 0
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
' ** Checks to see if the value of the ProgressBar will actually
' ** be changed by the CurrentPosition before making a call to
' ** UpdateProgress. If the value doesn't change, don't waste
' ** the call
If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(CurrentProgress)
End If
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress
method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements. Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress()
End If
End If
End Sub
Private Sub UpdateApplicationProgress()
Dim Changed As Boolean = False
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not CurrentStatusText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
If ApplicationStatusLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationStatusLabel.Text = CurrentStatusText
End If
End If
If Not ApplicationToolStripLabel Is Nothing Then
If ApplicationToolStripLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationToolStripLabel.Text = CurrentStatusText
End If
End If
End If
If Not ApplicationProgressBar Is Nothing Then
If ApplicationProgressBar.Value <> CurrentProgress Then
Changed = True
ApplicationProgressBar.Value = CurrentProgress
End If
End If
End With
If Changed Then
ApplicationForm.Refresh()
End If
Application.DoEvents()
End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
2
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
1
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before callingUpdateProgress
, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!
– G_Hosa_Phat
Nov 21 '18 at 18:20
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
1
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54
add a comment |
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
' ** FIRST ATTEMPT
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** FIRST ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 2:12.07
' ** Lines Written: 65358
' ** File Size: 8,166 KB
' ** SECOND ATTEMPT
stopWatch.Start()
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
TestOutputFile.Write(.ReadToEndAsync.Result)
End Using
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** SECOND ATTEMPT
' ** Records Retrieved: 65358
' ** Time To Complete: 1:04.01
' ** Lines Written: 65358
' ** File Size: 8,102 KB
End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress
method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code. The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async
method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
' ** METHOD 3
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
End Using
' ** METHOD 3 RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:15.07
' ** Lines Written: 34
' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker
, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress
method which caused the application to completely skip the actual dumping process. I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions. This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm
class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
Public Class FormHandler
Implements IDisposable
Public Property ApplicationForm As Form
Public Property ApplicationStatusLabel As Label
Public Property ApplicationToolStripLabel As ToolStripStatusLabel
Public Property ApplicationProgressBar As ProgressBar
Private LabelVisibleState As Boolean = True
Private ProgressBarVisibleState As Boolean = True
Private CurrentStatusText As String
Private CurrentProgress As Integer
Public Sub New(ByVal AppForm As Form)
ApplicationForm = AppForm
End Sub
Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String)
CurrentStatusText = StatusText
CurrentProgress = 0
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub ResetProgressUpdate()
CurrentStatusText = ""
CurrentProgress = 0
UpdateStatus()
End Sub
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress(CurrentStatusText)
End If
End If
End Sub
Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not ProgressText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Text = ProgressText
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Text = ProgressText
End If
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Value = CurrentProgress
End If
End With
ApplicationForm.Refresh()
Application.DoEvents()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Not ApplicationForm Is Nothing Then
ApplicationForm.Dispose()
End If
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Visible = LabelVisibleState
ApplicationStatusLabel.Dispose()
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Visible = LabelVisibleState
ApplicationToolStripLabel.Dispose()
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Visible = ProgressBarVisibleState
ApplicationProgressBar.Dispose()
End If
End Sub
End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress
variable as an Integer
), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
Dim CurrentProgress As Integer = 0
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
' ** Checks to see if the value of the ProgressBar will actually
' ** be changed by the CurrentPosition before making a call to
' ** UpdateProgress. If the value doesn't change, don't waste
' ** the call
If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(CurrentProgress)
End If
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress
method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements. Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress()
End If
End If
End Sub
Private Sub UpdateApplicationProgress()
Dim Changed As Boolean = False
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not CurrentStatusText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
If ApplicationStatusLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationStatusLabel.Text = CurrentStatusText
End If
End If
If Not ApplicationToolStripLabel Is Nothing Then
If ApplicationToolStripLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationToolStripLabel.Text = CurrentStatusText
End If
End If
End If
If Not ApplicationProgressBar Is Nothing Then
If ApplicationProgressBar.Value <> CurrentProgress Then
Changed = True
ApplicationProgressBar.Value = CurrentProgress
End If
End If
End With
If Changed Then
ApplicationForm.Refresh()
End If
Application.DoEvents()
End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
' ** FIRST ATTEMPT
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** FIRST ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 2:12.07
' ** Lines Written: 65358
' ** File Size: 8,166 KB
' ** SECOND ATTEMPT
stopWatch.Start()
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
TestOutputFile.Write(.ReadToEndAsync.Result)
End Using
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** SECOND ATTEMPT
' ** Records Retrieved: 65358
' ** Time To Complete: 1:04.01
' ** Lines Written: 65358
' ** File Size: 8,102 KB
End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress
method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code. The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async
method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
' ** METHOD 3
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
End Using
' ** METHOD 3 RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:15.07
' ** Lines Written: 34
' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker
, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress
method which caused the application to completely skip the actual dumping process. I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions. This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm
class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
Public Class FormHandler
Implements IDisposable
Public Property ApplicationForm As Form
Public Property ApplicationStatusLabel As Label
Public Property ApplicationToolStripLabel As ToolStripStatusLabel
Public Property ApplicationProgressBar As ProgressBar
Private LabelVisibleState As Boolean = True
Private ProgressBarVisibleState As Boolean = True
Private CurrentStatusText As String
Private CurrentProgress As Integer
Public Sub New(ByVal AppForm As Form)
ApplicationForm = AppForm
End Sub
Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String)
CurrentStatusText = StatusText
CurrentProgress = 0
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub ResetProgressUpdate()
CurrentStatusText = ""
CurrentProgress = 0
UpdateStatus()
End Sub
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress(CurrentStatusText)
End If
End If
End Sub
Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not ProgressText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Text = ProgressText
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Text = ProgressText
End If
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Value = CurrentProgress
End If
End With
ApplicationForm.Refresh()
Application.DoEvents()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Not ApplicationForm Is Nothing Then
ApplicationForm.Dispose()
End If
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Visible = LabelVisibleState
ApplicationStatusLabel.Dispose()
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Visible = LabelVisibleState
ApplicationToolStripLabel.Dispose()
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Visible = ProgressBarVisibleState
ApplicationProgressBar.Dispose()
End If
End Sub
End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress
variable as an Integer
), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
Dim CurrentProgress As Integer = 0
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
' ** Checks to see if the value of the ProgressBar will actually
' ** be changed by the CurrentPosition before making a call to
' ** UpdateProgress. If the value doesn't change, don't waste
' ** the call
If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(CurrentProgress)
End If
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress
method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements. Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress()
End If
End If
End Sub
Private Sub UpdateApplicationProgress()
Dim Changed As Boolean = False
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not CurrentStatusText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
If ApplicationStatusLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationStatusLabel.Text = CurrentStatusText
End If
End If
If Not ApplicationToolStripLabel Is Nothing Then
If ApplicationToolStripLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationToolStripLabel.Text = CurrentStatusText
End If
End If
End If
If Not ApplicationProgressBar Is Nothing Then
If ApplicationProgressBar.Value <> CurrentProgress Then
Changed = True
ApplicationProgressBar.Value = CurrentProgress
End If
End If
End With
If Changed Then
ApplicationForm.Refresh()
End If
Application.DoEvents()
End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
vb.net npgsql
edited Nov 21 '18 at 19:46
G_Hosa_Phat
asked Nov 21 '18 at 18:05
G_Hosa_PhatG_Hosa_Phat
287417
287417
2
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
1
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before callingUpdateProgress
, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!
– G_Hosa_Phat
Nov 21 '18 at 18:20
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
1
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54
add a comment |
2
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
1
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before callingUpdateProgress
, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!
– G_Hosa_Phat
Nov 21 '18 at 18:20
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
1
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54
2
2
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
1
1
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling
UpdateProgress
, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!– G_Hosa_Phat
Nov 21 '18 at 18:20
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling
UpdateProgress
, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!– G_Hosa_Phat
Nov 21 '18 at 18:20
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
1
1
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54
add a comment |
1 Answer
1
active
oldest
votes
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
3
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
I've tried throwing the process into aBackgroundWorker
, but when it would make a call to myUpdateProgress
method, it would (apparently) throw an exception during theInvoke
method that I couldn't trap. I tried wrapping thatInvoke
method in aTry...Catch
block, same with theUpdateProgress
method, and even the entire code block, but theInvoke
call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for theBackgroundWorker
to "complete".
– G_Hosa_Phat
Nov 21 '18 at 19:22
2
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53418109%2flong-running-process-with-responsive-form-performance-improvements%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
3
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
I've tried throwing the process into aBackgroundWorker
, but when it would make a call to myUpdateProgress
method, it would (apparently) throw an exception during theInvoke
method that I couldn't trap. I tried wrapping thatInvoke
method in aTry...Catch
block, same with theUpdateProgress
method, and even the entire code block, but theInvoke
call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for theBackgroundWorker
to "complete".
– G_Hosa_Phat
Nov 21 '18 at 19:22
2
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
add a comment |
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
3
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
I've tried throwing the process into aBackgroundWorker
, but when it would make a call to myUpdateProgress
method, it would (apparently) throw an exception during theInvoke
method that I couldn't trap. I tried wrapping thatInvoke
method in aTry...Catch
block, same with theUpdateProgress
method, and even the entire code block, but theInvoke
call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for theBackgroundWorker
to "complete".
– G_Hosa_Phat
Nov 21 '18 at 19:22
2
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
add a comment |
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
answered Nov 21 '18 at 18:34
the_lotusthe_lotus
10.1k12246
10.1k12246
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
3
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
I've tried throwing the process into aBackgroundWorker
, but when it would make a call to myUpdateProgress
method, it would (apparently) throw an exception during theInvoke
method that I couldn't trap. I tried wrapping thatInvoke
method in aTry...Catch
block, same with theUpdateProgress
method, and even the entire code block, but theInvoke
call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for theBackgroundWorker
to "complete".
– G_Hosa_Phat
Nov 21 '18 at 19:22
2
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
add a comment |
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
3
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
I've tried throwing the process into aBackgroundWorker
, but when it would make a call to myUpdateProgress
method, it would (apparently) throw an exception during theInvoke
method that I couldn't trap. I tried wrapping thatInvoke
method in aTry...Catch
block, same with theUpdateProgress
method, and even the entire code block, but theInvoke
call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for theBackgroundWorker
to "complete".
– G_Hosa_Phat
Nov 21 '18 at 19:22
2
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
3
3
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
I've tried throwing the process into a
BackgroundWorker
, but when it would make a call to my UpdateProgress
method, it would (apparently) throw an exception during the Invoke
method that I couldn't trap. I tried wrapping that Invoke
method in a Try...Catch
block, same with the UpdateProgress
method, and even the entire code block, but the Invoke
call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the BackgroundWorker
to "complete".– G_Hosa_Phat
Nov 21 '18 at 19:22
I've tried throwing the process into a
BackgroundWorker
, but when it would make a call to my UpdateProgress
method, it would (apparently) throw an exception during the Invoke
method that I couldn't trap. I tried wrapping that Invoke
method in a Try...Catch
block, same with the UpdateProgress
method, and even the entire code block, but the Invoke
call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the BackgroundWorker
to "complete".– G_Hosa_Phat
Nov 21 '18 at 19:22
2
2
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53418109%2flong-running-process-with-responsive-form-performance-improvements%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
1
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling
UpdateProgress
, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!– G_Hosa_Phat
Nov 21 '18 at 18:20
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
1
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54