Very inefficient LTP Listener thread - How can I make it moreefficient?

T

Tom

I have been running the CLR Profiler to find out where our memory leak
issues are. After some analysis, I found the #1 memory hog was in our
TCP listener thread. It took me a second to figure out what was so
wrong with it. Here is a look, can you spot it:

While m_bRunning

Try
' See if there is pending data in the socket buffer
If server.Pending Then

' If there is pending data, keep looping until
there is no more pending data
While server.Pending

' Perform a blocking call to accept requests
Dim client As System.Net.Sockets.TcpClient =
server.AcceptTcpClient()

data = Nothing

' Get a stream object for reading and writing
Dim stream As System.Net.Sockets.NetworkStream
= client.GetStream()

'Check to see if this Network Stream is
readable
If stream.CanRead Then

Dim bLoop As Boolean = True

'Wait for data to be available
While bLoop
'Check to see if data is available
If (stream.DataAvailable = True) Then
bLoop = False

If (System.DateTime.Now >
dtgLastResponseTimer) Then bLoop = False
End While

Dim txtXML As String = ""
Dim intTotalBytes As Int64 = 0
'If timeout was not reached, read from
stream
Do
Dim bytes(client.ReceiveBufferSize) As
Byte

' Loop to receive all the data sent by
the client.
Dim i As Int32 = 0
i = stream.Read(bytes, 0,
bytes.Length)
intTotalBytes = intTotalBytes + i

'Call WriteToDebug("Read " & i & "
bytes")

'Read contents of Request file
txtXML = txtXML &
System.Text.Encoding.ASCII.GetString(bytes, 0, i)

Loop While stream.DataAvailable

'Call WriteToDebug("Received " &
intTotalBytes & " total bytes")

'Ensure we have the entire message
Call ProcessRequest(txtXML, stream)

Else
'No data to read
Call WriteToDebug("Cannot read from
Network stream ")
End If

' Shutdown and end connection
client.Close()
stream.Close()

'Call WriteToDebug("Connection to client
closed: " + Format(System.DateTime.Now, "hh:mm:ss MMM-dd-yyyy"))
End While

Else
' There are no pending events, so pause
'Application.DoEvents()

System.Threading.Thread.Sleep(250)
End If
Catch
End Try

'ErrorHandler:
' If (Err.Number <> 0) Then
' Call WriteToDebug("SNMP Web Service
Startup Error:" & Err.Description)
' Err.Clear()
' End If
End While


Well, I know there were a LOT of byte being created, and I then saw:

Dim bytes(client.ReceiveBufferSize) As Byte

I did not know this but the buffer value is fixed at 32768! So for
the 50+ messages that came in, 1.7Meg of memory was being allocated.
The GC was running constantly. Well, looking at other examples, I
noticed that others have done the same thing. So does anyone know a
more efficient method to read data off of the socket. I would
obviously only use a single byte[] for reuse, but is there something
simpler?

Tom


P.S. - I did not write this code
 
G

Guest

Simpler how? The obvious mistake is the creation of that buffer inside the
while loop. It should be outside the loop so either move it, or just
declare it outside the loop and create it only on the first pass. If 32k is
larger than you'll ever really be using, then make it a smaller number if
you want (though if it's 32k and only created once, it's probably a
non-issue).

You might do something like this (I'll leave it to you to convert to the
language of your choice)

byte[] bytes; // this will initialize to null

while(...)
{
if(bytes == null)
bytes = new byte[myBufferSize]; // create only once
else
bytes.Initialize(); // zero out the data for safety

// do your Rx logic here
}


--

Chris Tacke, eMVP
Join the Embedded Developer Community
http://community.opennetcf.com



Tom said:
I have been running the CLR Profiler to find out where our memory leak
issues are. After some analysis, I found the #1 memory hog was in our
TCP listener thread. It took me a second to figure out what was so
wrong with it. Here is a look, can you spot it:

While m_bRunning

Try
' See if there is pending data in the socket buffer
If server.Pending Then

' If there is pending data, keep looping until
there is no more pending data
While server.Pending

' Perform a blocking call to accept requests
Dim client As System.Net.Sockets.TcpClient =
server.AcceptTcpClient()

data = Nothing

' Get a stream object for reading and writing
Dim stream As System.Net.Sockets.NetworkStream
= client.GetStream()

'Check to see if this Network Stream is
readable
If stream.CanRead Then

Dim bLoop As Boolean = True

'Wait for data to be available
While bLoop
'Check to see if data is available
If (stream.DataAvailable = True) Then
bLoop = False

If (System.DateTime.Now >
dtgLastResponseTimer) Then bLoop = False
End While

Dim txtXML As String = ""
Dim intTotalBytes As Int64 = 0
'If timeout was not reached, read from
stream
Do
Dim bytes(client.ReceiveBufferSize) As
Byte

' Loop to receive all the data sent by
the client.
Dim i As Int32 = 0
i = stream.Read(bytes, 0,
bytes.Length)
intTotalBytes = intTotalBytes + i

'Call WriteToDebug("Read " & i & "
bytes")

'Read contents of Request file
txtXML = txtXML &
System.Text.Encoding.ASCII.GetString(bytes, 0, i)

Loop While stream.DataAvailable

'Call WriteToDebug("Received " &
intTotalBytes & " total bytes")

'Ensure we have the entire message
Call ProcessRequest(txtXML, stream)

Else
'No data to read
Call WriteToDebug("Cannot read from
Network stream ")
End If

' Shutdown and end connection
client.Close()
stream.Close()

'Call WriteToDebug("Connection to client
closed: " + Format(System.DateTime.Now, "hh:mm:ss MMM-dd-yyyy"))
End While

Else
' There are no pending events, so pause
'Application.DoEvents()

System.Threading.Thread.Sleep(250)
End If
Catch
End Try

'ErrorHandler:
' If (Err.Number <> 0) Then
' Call WriteToDebug("SNMP Web Service
Startup Error:" & Err.Description)
' Err.Clear()
' End If
End While


Well, I know there were a LOT of byte being created, and I then saw:

Dim bytes(client.ReceiveBufferSize) As Byte

I did not know this but the buffer value is fixed at 32768! So for
the 50+ messages that came in, 1.7Meg of memory was being allocated.
The GC was running constantly. Well, looking at other examples, I
noticed that others have done the same thing. So does anyone know a
more efficient method to read data off of the socket. I would
obviously only use a single byte[] for reuse, but is there something
simpler?

Tom


P.S. - I did not write this code
 
T

Tom

Simpler how? The obvious mistake is the creation of that buffer inside the
while loop. It should be outside the loop so either move it, or just
declare it outside the loop and create it only on the first pass. If 32k is
larger than you'll ever really be using, then make it a smaller number if
you want (though if it's 32k and only created once, it's probably a
non-issue).

You might do something like this (I'll leave it to you to convert to the
language of your choice)

byte[] bytes; // this will initialize to null

while(...)
{
if(bytes == null)
bytes = new byte[myBufferSize]; // create only once
else
bytes.Initialize(); // zero out the data for safety

// do your Rx logic here

}

--

Chris Tacke, eMVP
Join the Embedded Developer Communityhttp://community.opennetcf.com


I have been running the CLR Profiler to find out where our memory leak
issues are. After some analysis, I found the #1 memory hog was in our
TCP listener thread. It took me a second to figure out what was so
wrong with it. Here is a look, can you spot it:
While m_bRunning
Try
' See if there is pending data in the socket buffer
If server.Pending Then
' If there is pending data, keep looping until
there is no more pending data
While server.Pending
' Perform a blocking call to accept requests
Dim client As System.Net.Sockets.TcpClient =
server.AcceptTcpClient()
data = Nothing
' Get a stream object for reading and writing
Dim stream As System.Net.Sockets.NetworkStream
= client.GetStream()
'Check to see if this Network Stream is
readable
If stream.CanRead Then
Dim bLoop As Boolean = True
'Wait for data to be available
While bLoop
'Check to see if data is available
If (stream.DataAvailable = True) Then
bLoop = False
If (System.DateTime.Now >
dtgLastResponseTimer) Then bLoop = False
End While
Dim txtXML As String = ""
Dim intTotalBytes As Int64 = 0
'If timeout was not reached, read from
stream
Do
Dim bytes(client.ReceiveBufferSize) As
Byte
' Loop to receive all the data sent by
the client.
Dim i As Int32 = 0
i = stream.Read(bytes, 0,
bytes.Length)
intTotalBytes = intTotalBytes + i
'Call WriteToDebug("Read " & i & "
bytes")
'Read contents of Request file
txtXML = txtXML &
System.Text.Encoding.ASCII.GetString(bytes, 0, i)
Loop While stream.DataAvailable
'Call WriteToDebug("Received " &
intTotalBytes & " total bytes")
'Ensure we have the entire message
Call ProcessRequest(txtXML, stream)
Else
'No data to read
Call WriteToDebug("Cannot read from
Network stream ")
End If
' Shutdown and end connection
client.Close()
stream.Close()
'Call WriteToDebug("Connection to client
closed: " + Format(System.DateTime.Now, "hh:mm:ss MMM-dd-yyyy"))
End While
Else
' There are no pending events, so pause
'Application.DoEvents()
System.Threading.Thread.Sleep(250)
End If
Catch
End Try
'ErrorHandler:
' If (Err.Number <> 0) Then
' Call WriteToDebug("SNMP Web Service
Startup Error:" & Err.Description)
' Err.Clear()
' End If
End While
Well, I know there were a LOT of byte being created, and I then saw:
Dim bytes(client.ReceiveBufferSize) As Byte
I did not know this but the buffer value is fixed at 32768! So for
the 50+ messages that came in, 1.7Meg of memory was being allocated.
The GC was running constantly. Well, looking at other examples, I
noticed that others have done the same thing. So does anyone know a
more efficient method to read data off of the socket. I would
obviously only use a single byte[] for reuse, but is there something
simpler?

P.S. - I did not write this code

Thanks Chris,

You are a great help!

Tom Kuhn
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top