PDA

View Full Version : In which thread are TCP IO-Operations done?



P@u1
1st June 2011, 19:36
Hi,

I am wondering about how TCP IO-Operations are done in QT, especially, in which thread they are executed.

In the docs i found;

The bytes are written when control goes back to the event loop

But in which thread the actual writing is done?
If it is done by the even-loop-owning thread (probably the main/gui thread), then if a lot of data is to be written to the socket, the gui could still freeze.
Or is there some invisible background thread, which does the actual IO-processing for me?
In this case using an additional thread for networking is useless, as it doesen't boost performance at all, right?

wysota
1st June 2011, 19:55
Qt doesn't use any separate threads for networking.


Or is there some invisible background thread, which does the actual IO-processing for me?
No, there is no such thing.

In this case using an additional thread for networking is useless, as it doesen't boost performance at all, right?
Using an additional thread is useless but for other reasons.

Santosh Reddy
1st June 2011, 19:58
each thread (if based on QThread) have there own event loop (if exec() is called from run), then in which ever thread TCP object is decrated, it's IO is done in that perticular thread.

If you use additional thread for networking then it is better to declared the TCP object in that thread.

P@u1
1st June 2011, 20:02
Using an additional thread is useless but for other reasons.

Thx for your answer.

Could you please tell which reasons these are?

I thought that if there is a lot of network traffic it can consume precious time needed for other things.
Probably usually things will be fast enough anyway, but theoretically a new thread can help out in this case.

wysota
1st June 2011, 20:58
Could you please tell which reasons these are?
You can find a lot of my and other people's explanations on the subject on this forum.


I thought that if there is a lot of network traffic it can consume precious time needed for other things.
Ok, let's pursue this statement. How much traffic is "a lot of network traffic"? A typical (Internet) network link ranges from 512kbps to say 8Mbps. So to totally saturate such a link a constant flow of 1 megabyte of data per second is required. Modern computers have processing capacities of say 4G operations per second (per execution unit) so on average the computer can afford 4000 cycles to process one byte of data. I'd say this is quite a lot. So what should happen during those 4k cycles?

When data arrives to the network chip, it gets checked for errors (and potentially rejected), buffered and hardware signals the operating system there is data to be read. The operating system reads the data, strips headers from it (which reduces the amount of data), discovers which pipe the data belongs to, stores the data in the pipe buffer and raises a flag that data can be read from the pipe. All this happens outside the application, so if the machine has more than one processing unit, the speed of a single threaded application can theoretically not be influenced at all and on the other hand when the app is multithreaded, the system might get less time to do the task I just described without any benefit for the application (because processing takes place outside the application, in kernel space).

What happens in the application then? Networking in Qt is implemented using QSocketNotifier class. Each notifier, when created, gets registered in the event dispatcher (each thread has its own). When the dispatcher processes events (like when QCoreApplication::processEvents() is called) at some point registered notifiers get queried using a dedicated system call (which could be select() or epoll() on Linux or maybe some other mechanism on other systems) which of them are "active". I don't know much about other solutions so I will focus on select(). You pass it a set of descriptors and a structure containing information how long should the call potentially wait until some socket becomes active. Then all socket notifiers that are associated with active descriptors emit their signals. The data is potentially copied to internal application buffers so that more data can arrive to the socket and readyRead() signal is emitted. Here "network processing" ends.

So what happens if you have all the sockets in the same thread? A single select() call is made, it returns quite fast (since there is a good chance at least one of the 100 sockets received data), data (maximum of ~1MB as stated above) is copied and that's it. I'm sure it takes much much less than 4k cycles per byte.

What happens if you have 100 threads with a single socket in each thread? 100 select() calls are made, each potentially waiting the maximum time alloted for select() in the event dispatcher and doing exactly nothing. That's the worst case scenario -- the best case scenario is that each of the threads gets signalled, each of them reads 10kB of data (1MB/100), signals and goes to sleep again. So you waste resources for 100 threads, do context switching 100 times per second (where each switch costs about 1k-2k cycles at least which sums up to 100k cycles for one single application) only to read 10kB which probably takes much less time (especially if it's enough to just change ownership of memory pages containing the data, then it's probably a matter of a couple of cycles as the rest is done by the MMU, if not, probably DMA can somehow be used -- I don't know, it's not my domain, just guessing). Now imagine how much useful work could those 100 threads do during this time instead of just sleeping on select(). How about spawning 100 threads to process network data received by the main thread that does nothing most of the time anyway (I mean... how fast can you click on buttons in your app)? Which is better?


Probably usually things will be fast enough anyway, but theoretically a new thread can help out in this case.
No, theoretically it can only make things worse, as shown above.

Santosh Reddy
1st June 2011, 21:40
Threads will help to you in dedicated tasks (like large image / data processing) which requires dedicated procesor time (processor intensive tasks), having dedicated thread will avoid the freezing of your GUI with the thread being run in background (atleast it appeast to be so)

P@u1
21st June 2011, 09:57
You almost entirely talked about receiving, I'm more concerned with sending.
When I opened this thread I didn't know the concept of asynchronous IO, so I was talking about threads, maybe that was not very clear.

Does QT use asynchronous IO or synchronous IO?
If you network has an upload of 1MB/s and you want to send 1MB and QT does synchronous IO so it would block for 1 second, wouldn't it?
This would be inacceptable.
I know that 1 mb is a lot to send, but the problem is that IO operations usually take orders of magnitude longer than normal operations if they are done synchronously.

wysota
21st June 2011, 10:02
If you network has an upload of 1MB/s and you want to send 1MB and QT does synchronous IO so it would block for 1 second, wouldn't it?
No. The data would be stored in the system buffer and that's where the role of your application ends so the call returns and the operating system and its TCP stack takes it from here. Once TCP packet is usually small (unless special options are used) so the data wouldn't be sent in one go anyway, it has to be stored in an intermediate buffer and your app has nothing to do with it. In theory it can take a few hours for the data to be flowing through the link (especially with retransmissions) and if the application was blocked for this whole time, it wouldn't make any sense.

P@u1
21st June 2011, 11:08
What is the sense of async IO for sending then?
I used it with boost asio
boost::asio::udp::socket.async_send_to

Or lets rather say what is the difference between async and sync IO for sending?

Edit:
And another thing which I thought is that one of the reasons for using synchronous IO is that it blocks until the actual IO process is finished to prevent flooding the network.
This is not the case anymore?

wysota
21st June 2011, 13:02
What is the sense of async IO for sending then?
That you don't wait for the system to transfer the data into its buffers. Qt's sockets are asynchronous, so it's likely same thing happens here but you'd have to verify that in the source code. Look for where bytesWritten() is emitted for the socket object.


And another thing which I thought is that one of the reasons for using synchronous IO is that it blocks until the actual IO process is finished to prevent flooding the network.
This is not the case anymore?
There is no such thing as "flooding the network". Your application doesn't have any connections to the network, only to the network stack of your operating system. Once the socket buffer fills completely, writing to the socket will block for sync IO and will return an error for async IO. Preventing network congestion is done by a mechanism called "TCP Window" and smoothed retransmission timer (in case of the TCP protocol of course). Limited buffer size is a defensive mechanism of the OS (so that you don't run out of resources) and not the network.