|
Mark:
Thank you.
Yes, it helped quite a bit in that it confirmed my suspicions about CSocket and CAsyncSocket...and IOCP.
May I elicit a few more comments from you?
o In your example, 1000 threads serving 1000 connections makes a strong case for thread pooling (IOCP). It would be equally pointless to inherit the overhead of thread pooling for a single connection. Where do you see the break-even point? 10? 100? I know there can be no precise answer; it depends on many things, like processsor speeds, HyperThreading, Dual-core, multi processors, etc.
o Overlap, asynchronous and non-blocking kind of address the same issue--that being performance. What is the case to be made for each technique? Other than the thread pooling issue, is there a performance benefit over using non-blocking sockets and periodically polling them?
o What can you tell me about select, WSAAsyncSelect, etc.? I've had very limited success with these. It seems I need to make repeated calls to get the status, but when I read about things like WSAAsyncSelect I get the impression that the message is sent without having to poll for it.
-Obi Wan 2
|
|
|
|
|
Obi Wan 2 wrote: In your example, 1000 threads serving 1000 connections makes a strong case for thread pooling (IOCP).
It would be equally pointless to inherit the overhead of thread pooling for a single connection.
Where do you see the break-even point? 10? 100?
To me, the break-even point is at the number of processors on the machine. There's no reason
to have more threads than can be run concurrently. I get good results with IOCP and the number
of threads in the pool equal to number-of-processors * 2. This way there's a thread available
to start processing until it needs to wait for another thread to finish using a a relatively
slow-to-access shared resource (a database in my case). When another thread finishes, the next
thread is already waiting at the code that accesses the resource. I haven't yet been able to
thoroughly test performance using 1, 2, ... threads-per-processor to determine the best
performance but it's something that should be done.
I agree for a single connection an IOCP with a pool of threads would be pointless, unless you're
using the IOCP for other things besides socket I/O. IOCPs make a handy semaphore-like object
with no release count limit, but that's another topic altogether.
There's nothing wrong with using an IOCP with just one thread. If, for example, an app does many
socket sends that don't require a reply, then putting the messages in overlapped buffers and
sending using the IOCP can save coding time since IOCP has implemented queueing for you. The
one thread just cleans up the buffers when the IOCP is done with them (by deleting or maybe
returning them to a buffer pool). There's also the advantage in this scenario of being able
to turn off the socket's send buffering, since overlapped buffers hold the data - no need to
do an extra copy to the socket's send buffer.
Obi Wan 2 wrote: Overlap, asynchronous and non-blocking kind of address the same issue--that being performance.
What is the case to be made for each technique? Other than the thread pooling issue, is there a
performance benefit over using non-blocking sockets and periodically polling them?
I would say that it depends on the architecture of the code, specifically how threads are being
used. A single-threaded application using blocking sockets would perform poorly and if it had
a UI, the user experience would be horrible. Overlapped/asynchronous operations can help by
allowing processing to continue immediately after a socket operation is called. When the
operation finishes at a later time then the app is notified. Because the Windows Sockets
implementation is capable of being used event-driven, I think polling is completely unnecessary,
a waste of CPU cycles, and inefficient...which leads to the 3rd topic...
Obi Wan 2 wrote: What can you tell me about select, WSAAsyncSelect, etc.?
First, the only difference between WSAAsyncSelect and WSAEventSelect is the method that the app
is notified that a particular socket event occurs. WSAAsyncSelect uses regular Windows
messages (good for single-thread apps) and WSAEventSelect uses an event synchronization object
(good for multi-threaded apps). Waiting for FD_READ events is particularly useful because it
eliminates the need to poll for received data.
It's typically recommended that one thread be used for socket I/O. Sockets are not thread-safe
(although reading and writing simultaneously to a Windows Socket generally works, it's not
documented as being safe) so multiple threads would end up waiting to use the socket anyway.
For maximum efficiency using TCP it's most important to keep the receive buffer flushed,
otherwise performance drops significantly. If an app isn't able to process incoming data as
fast as it is received then the data should be queued/buffered for later processing by another
thread to allow the I/O thread to continue receiving data. Actually that applies to UDP as well
except that with UDP, if you don't receive datagrams fast enough, it only affects the receiving
end. With TCP the sender is affected because it knows the receiving end can't accept data so it
has to wait.
For more specific implementation details, I guess it comes down to what type of communication is
going on. Text data to/from a database requires a relatively simple implementation. Real-time,
high bandwidth data requires something more sophisticated. IOCPs are great for managing large
numbers of connections, such as with a large organization's database server or an HTTP server.
I'm no expert on this stuff. All my comments are based on experience - A major portion of my
application suite is real-time streaming of video/audio/device-control data mixed with database
communication. I've had the (un?)fortunate pleasure of re-writing my communication code
several times over the past few years trying different combinations of socket operations and
thread models.
Mark
|
|
|
|
|
I have created a report list view with columns sized so horizontal scrollbar does not appear. I respond to the WM_SIZE message to change the column with, but sometimes a spurious horizontal scrollbar from appears. As soon as you try to use it, it goes away. (It also doesn't use themes in XP!)
I've tried several methods to stop this from happening, but am at a loss.
I even resorted to putting this in the OnSize() handler:
<code> if (m_listCtrl.m_hWnd)
{
DWORD style = GetWindowLong(m_listCtrl.m_hWnd, GWL_STYLE);
if (style & WS_HSCROLL)
SetWindowLong(m_listCtrl.m_hWnd,
GWL_STYLE, (style & ~WS_HSCROLL));
}
</code>
I can break on the SetWindowLong() call, but it doesn't actually get rid of the scroll bar.
-- modified at 19:01 Friday 26th January, 2007
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
Joe Woodbury wrote: prevent a horizontal scrollbar from appearing in list view.
You may want to consider resizing the list view's column(s) to prevent the horizontal scrollbar from being displayed. This should be done when the list view's size changes. The disadvantage is that your users may often need to resize the columns manually.
It's less of an issue if the control has a single column, in which case the user will simply resize the application's window (or view).
/ravi
|
|
|
|
|
I guess I wasn't clear enough. I have sized the columns to fit without a horizontal bar being displayed. When sizing, though, a bar sometimes does appear. (And no, the users aren't going to be resizing the columns manually--just trust me on that.)
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
Sorry, I should've read your message carefully.
You may want to try SetRedraw(FALSE); before resizing the columns, and SetRedraw(TRUE); followed by an Invalidate() and UpdateWindow() pair to force a clean repaint after the resize logic.
/ravi
|
|
|
|
|
Tried that.
I believe that what happens is that the size message hits the internal logic first. It sees it need to put up a scroll bar and does. It then gets the message to resize the column and so it gets rid of the scroll bar but not always.
My gut feeling now is that I have to superclass SysListView32. Don't want to do that, so will just ignore the problem for now.
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
In this[^] app, I cheated by momentarily hiding/showing the list control. The blink is virtually unnoticeable. I admit it's a cheesy solution workaround.
/ravi
|
|
|
|
|
Joe Woodbury wrote: I respond to the WM_SIZE message to change the column with, but sometimes a spurious horizontal
scrollbar from appears.
What if, when resizing the columns, you use LVSCW_AUTOSIZE_USEHEADER for the cx param in the
LVM_SETCOLUMNWIDTH/ListView_SetColumnWidth() call for the last column?
Mark
|
|
|
|
|
Mark Salsbery wrote: What if, when resizing the columns, you use LVSCW_AUTOSIZE_USEHEADER for the cx param in the
LVM_SETCOLUMNWIDTH/ListView_SetColumnWidth() call for the last column?
I am.
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
Where's the OnSize handler that you change the column widths in - The parent window or a class
derived from CListView?
If the scrollbar goes away right when you try to use it is it just a matter of refreshing with
Invalidate()/UpdateWindow()?
Mark
|
|
|
|
|
It doesn't matter where I put the OnSize handler.
The scrollbar doesn't go away right away, only if I actually click it.
As I said to Ravi, I think I'll have to superclass the window and handle WM_SIZE BEFORE the control gets it.
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
Joe Woodbury wrote: As I said to Ravi, I think I'll have to superclass the window and handle WM_SIZE BEFORE the control gets it.
Since you're using MFC it's easy to try it. Just derive a class from CListView and add the
WM_SIZE handler. Do your stuff in OnSize before passing control to the base class.
Mark
|
|
|
|
|
I ran across an article that suggests when MFC superclasses SysListView32, that what I'm getting is faux superclass, not a genuine one:
http://www.pocketpcdn.com/articles/child_list.html[^]
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
BTW, have you tried listview.Invalidate(); listview.UpdateWindow() after resizing columns in a
WM_SIZE handler? This needs to be done on windows with scroll bars so the scrollbars redraw
properly. Maybe that applies to the listview control as well?
*EDIT* I'm talking from the parent's OnSize(), where presumably you are resizing the listview
and the listview's columns. Rarely IME does a control need to be sub/superclassed for WM_SIZE
notification.
Mark
|
|
|
|
|
Doesn't work.
I tried using in the derived control class since I'm already deriving it for other reasons.
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
I found a weird solution. It still results in horizontal scroll bar flash, but I haven't gotten the bar to stick yet.
In short, in the dialog I process WM_SIZING and do the following:
DWORD style = GetWindowLong(m_hWnd, GWL_STYLE);<br />
int scrollWidth = (style & WS_VSCROLL) ? ::GetSystemMetrics(SM_CXVSCROLL) - 2 : 2;<br />
<br />
CRect rect;<br />
m_listCtrl.GetClientRect(rect);<br />
m_listCtrl.SetColumnWidth(CSpyListCtrl::DATA_COLUMN, rect.right - CSpyListCtrl::TIME_COLUMN_WIDTH - CSpyListCtrl::TOPIC_COLUMN_WIDTH - scrollWidth);
When processing the WM_SIZE message (inside the instance of my [C++]subclassed list control, I do the following:
SetColumnWidth(CSpyListCtrl::DATA_COLUMN, cx - TIME_COLUMN_WIDTH - TOPIC_COLUMN_WIDTH - 2);
(I can't use LVSCW_AUTOSIZE_USEHEADER in OnSizing() else the bar sticks. I opted to not use it for OnSize() to keep the width the same.)
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
I guess whatever works for you
If the listview is a child of another window then it is easier IMO to do this stuff from the
parent.
The only reason a scroll bar should appear is if at the time of drawing the column widths exceeds
the size of the window. I've never seen a horizontal scrollbar for no reason
Mark
|
|
|
|
|
Ah, it doesn't show up for no reason. It shows up when resizing and during that resizing operation the column width is momentarily bigger that the size of the window BEFORE its adjusted.
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
Joe Woodbury wrote: Ah, it doesn't show up for no reason. It shows up when resizing and during that resizing operation the column width is momentarily bigger that the size of the window BEFORE its adjusted.
That's not spurious So, to get me on the same page (apparently I'm not even in the
right book), do you want the scrollbar to not show while it's resizing or do you want the
scrollbar to go away when the resize is finished or both?
|
|
|
|
|
It's spurious in that what I am doing should mean it never shows up. It's also spurious in that when it shows up, clicking on it makes it go away. Even more curious, it doesn't always use XP themes when it shows up. (I'm fairly sure this is a subtle bug in the common control library; I do know it's been around for quite a while, it's just that the nature of my current app makes it especially annoying.)
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
OK, with the following code I never see any horizontal scrollbar (Windows XP Pro, SP2).
This is a simple dialog with only a listview control - nothing else. Border is resizing.
I used a dialog because it was quick to toss together in VC++.
#include "stdafx.h"
#include "MFCTester.h"
#include "TestListViewDialog.h"
#include ".\testlistviewdialog.h"
IMPLEMENT_DYNAMIC(CTestListViewDialog, CDialog)
CTestListViewDialog::CTestListViewDialog(CWnd* pParent )
: CDialog(CTestListViewDialog::IDD, pParent)
{
}
CTestListViewDialog::~CTestListViewDialog()
{
}
void CTestListViewDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, m_ListCtrl);
}
BEGIN_MESSAGE_MAP(CTestListViewDialog, CDialog)
ON_WM_SIZING()
ON_WM_SIZE()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
BOOL CTestListViewDialog::OnInitDialog()
{
CDialog::OnInitDialog();
UpdateData(FALSE);
m_ListCtrl.InsertColumn(0, _T("Column 1"));
m_ListCtrl.InsertColumn(1, _T("Column 2"));
m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle() | LVS_EX_GRIDLINES);
FitListViewToDialog();
return TRUE;
}
void CTestListViewDialog::OnSizing(UINT fwSide, LPRECT pRect)
{
CDialog::OnSizing(fwSide, pRect);
if (pRect->right - pRect->left < 320)
pRect->right = pRect->left + 320;
if (pRect->bottom - pRect->top < 240)
pRect->bottom = pRect->top + 240;
}
void CTestListViewDialog::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if (cx > 0 && cy > 0)
{
FitListViewToDialog();
}
}
void CTestListViewDialog::FitListViewToDialog()
{
if (::IsWindow(m_ListCtrl))
{
CRect ClientRect;
GetClientRect(&ClientRect);
m_ListCtrl.MoveWindow(0, 0, ClientRect.Width(), ClientRect.Height(), TRUE);
m_ListCtrl.SetColumnWidth(0, ClientRect.Width() / 2);
m_ListCtrl.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);
}
}
BOOL CTestListViewDialog::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
-- modified at 19:29 Sunday 28th January, 2007
Added OnEraseBkgnd()
Added gridlines to check for flicker
-- modified at 19:38 Sunday 28th January, 2007
|
|
|
|
|
Here's the header file if you want to try it
#pragma once
#include "afxcmn.h"
class CTestListViewDialog : public CDialog
{
DECLARE_DYNAMIC(CTestListViewDialog)
public:
CTestListViewDialog(CWnd* pParent = NULL);
virtual ~CTestListViewDialog();
enum { IDD = IDD_DIALOG1 };
protected:
virtual void DoDataExchange(CDataExchange* pDX);
void FitListViewToDialog();
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
afx_msg void OnSizing(UINT fwSide, LPRECT pRect);
afx_msg void OnSize(UINT nType, int cx, int cy);
CListCtrl m_ListCtrl;
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};
|
|
|
|
|
I ran your code. I still got horizontal bar flashing, but the bar didn't stick around. My code was very similar, with one key difference (which you had warned me about and I had dismissed); I was changing the column size inside OnSize() of my subclassed CListCtrl.
Thanks for your help.
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke
|
|
|
|
|
I probably missed this in previous posts, but what version OS are you on? What version
is your common controls DLL?
Just for my own reference - I've gone to great lengths over the years to provide flicker-free
smooth non-spurious-crap UI so I'd like to know what other platforms to test on.
Thanks!
Mark
|
|
|
|