Jump to content
  • Advertisement
Sign in to follow this  
nethackpro

select without a linear search?

This topic is 3604 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Im trying to make some changes to my server side code to make it more efficient, so Ive been reading up more on select(). The problem is, all the examples I find do something like this after calling select
	if (FD_ISSET(sock,&socks))
		handle_new_connection();
	/* Now check connectlist for available data */
	
	/* Run through our sockets and check to see if anything
		happened with them, if so 'service' them. */

	for (listnum = 0; listnum < 5; listnum++) {
		if (FD_ISSET(connectlist[listnum],&socks))
			deal_with_data(listnum);
	}
}
My problem with this is the linear search it does. We know from our call to select how many sockets are ready to be read from, but isnt there a way to know specifically which ones they are off the bat without having to do a linear search? With this method, I'd have to search through every single connected player every time any of the clients send a packet to my server. Maybe theres something similar to select that returns a fd_set containing only the sockets ready for reading?

Share this post


Link to post
Share on other sites
Advertisement
Yes, there are much better alternatives, but they depend on your target OS and are not necessarily trivial to implement.

Seeing that your example has an entire 5 sockets, it is probably not worth to spend a lot of time worrying, because there is no really existent overhead with select(). Unless you plan for more than a hundred descriptors, select() works just as fine as everything else.

If you need several hundred or thousand descriptors, look into epoll() or kqueue() for unix-like system and IO completion ports for Windows.

Share this post


Link to post
Share on other sites
That example isnt from my code. I was simply showing an example of what the examples that are out there look like. But yeah, I'm on linux and want my game to have support for thousands of players.

Share this post


Link to post
Share on other sites
Then you want epoll. Note that while epoll can be confusing (read the docs very carefully!), it is flipping awesome, you can for example create special file descriptors that fire notifications when your thread catches a signal, and catch those with the same epoll call as you catch your socket notifications and your disk ready notifications.

If you don't want to mess with the low-level stuff yourself, there is libevent which does that for you. As with all layered libraries, it adds a little overhead (hardly noticeable) and does not do all the cool things that you could possibly do otherwise, but it works and it takes away 99.5% of the pain implementing it.

Share this post


Link to post
Share on other sites
thanks for the helpful reply. I plan on implementing it all myself as the main reason Im doing this project is to learn. I'm currently reading the man pages on epoll because I cant seem to find decent documentation on it online. Do you know of any sites that might be helpfull in learning the ins and outs of epoll?

Share this post


Link to post
Share on other sites
Boost has the "asio" library. It wraps the epoll/iocp calls into a single interface. Works the same on linux and windows.

It's not perfect, but it's portable, and event driven (no select search to do).

www.boost.org

Share this post


Link to post
Share on other sites
I agree; the epoll API is better than the select API for certain heavy use cases.

However, have you ever profiled this loop to be a problem? If you start getting more load, what will happen is that any time spent looping over inactive sockets will be time that the other users can add more data to their buffers, which means there will be more active users in the next iteration. The system is self-balancing that way, and under heavy load, the looping time just won't show up on a profile. Meanwhile, why would you optimize the light load case? :-)

Share this post


Link to post
Share on other sites
Ok, so I've got everything set up right going by the man pages, but it seems to be stuck on epoll_wait. It always returns 0 and I cant figure out why for the life of me. Im going to assume its because Ive been up for over 24 hours and made a really simple mistake somewhere but heres the code I got so far for it.

if((ReadFDs = epoll_wait(epFD, Events, MAX_PLAYERS, 0)) < -1)
{
perror("epoll_wait");
exit(EXIT_FAILURE);
}
// We've got some data to read //
else if(ReadFDs > 0)
{
for(int i = 0; i < ReadFDs; i++)
{
// New Connection //
if(Events.data.fd == Listener)
{
if((NewFD = accept(Listener, (struct sockaddr *)&Client, &CliLen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
SetNonBlocking(NewFD);
Event.events = EPOLLIN | EPOLLET;
Event.data.fd = NewFD;
if(epoll_ctl(epFD, EPOLL_CTL_ADD, NewFD, &Event) < 0)
{
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
send(NewFD, "Welcome to Paveria...\n", 22, 0);
}
// Handle Input //
else
{
}


The man page says this...

EXAMPLE FOR SUGGESTED USAGE
While the usage of epoll when employed like a Level Triggered interface
does have the same semantics of poll(2), an Edge Triggered usage
requires more clarifiction to avoid stalls in the application event
loop. In this example, listener is a non-blocking socket on which lis-
ten(2) has been called. The function do_use_fd() uses the new ready
file descriptor until EAGAIN is returned by either read(2) or write(2).
An event driven state machine application should, after having received
EAGAIN, record its current state so that at the next call to
do_use_fd() it will continue to read(2) or write(2) from where it
stopped before.

struct epoll_event ev, *events;

for(;;) {
nfds = epoll_wait(kdpfd, events, maxevents, -1);

for(n = 0; n < nfds; ++n) {
if(events[n].data.fd == listener) {
client = accept(listener, (struct sockaddr *) &local,
&addrlen);
if(client < 0){
perror("accept");
continue;
}
setnonblocking(client);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%d0,
client);
return -1;
}
}
else
do_use_fd(events[n].data.fd);
}
}


Do you see anything that I'm doing obviously wrong?

Share this post


Link to post
Share on other sites
Quote:
Original post by nethackpro
Do you see anything that I'm doing obviously wrong?


Umm, you're passing zero for the timeout, which make epoll_wait return immediately regardless of how many events are available? Do I win?

Share this post


Link to post
Share on other sites
sorry, but you dont win(I wish you did, believe me. Then I could be done with this and move on). When I try to connect it shouldn't return 0 anymore. However, I tried setting the value to -1 and > 0 earlier and it didnt help

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!