Problem with pipes

Discussion of chess software programming and technical issues.

Moderator: Ras

Tony

Re: Problem with pipes

Post by Tony »

bob wrote:
Tony wrote:The probleem does not seem to be the reading of the stop command, but responding to it. How fast does your engine stop after receiving it ?

Some people try to save time by only checking the stop condition at the top of the searchfunctions, wich slows down stopping seriously.
Tony
really should not hurt since things are recursive. This should see the stop condition at least once every node...
I "optimized" even more by not checking it in Quiescence. Not a good idea if you do check giving moves as well.

Checking it only every X nodes is another bad idea.

Tony
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: Problem with pipes

Post by sje »

Tony wrote:I "optimized" even more by not checking it in Quiescence. Not a good idea if you do check giving moves as well.

Checking it only every X nodes is another bad idea.
I say that all schemes that check based on node count are faulty because:

1) Excessive dependence on underlying processor speed,

2) Excessive dependence on a variable position processing time,

3) The much, much better alternative of setitimer() is available on Unix-like platforms.

Symbolic uses setitimer() in wall clock mode to support the low overhead, periodic updating of a global time tick counter. The rate currently in use is 100 Hz and that frequency is the same regardless of hardware or position complexity. A search thread only has to look at the counter and check it against the recorded value the last time a poll/trace/whatever happened. If N ticks have gone by since the last event, then it's time for another event. The setitimer() overhead is too low to be accurately measured.
Tony

Re: Problem with pipes

Post by Tony »

sje wrote:
Tony wrote:I "optimized" even more by not checking it in Quiescence. Not a good idea if you do check giving moves as well.

Checking it only every X nodes is another bad idea.
I say that all schemes that check based on node count are faulty because:

1) Excessive dependence on underlying processor speed,

2) Excessive dependence on a variable position processing time,

3) The much, much better alternative of setitimer() is available on Unix-like platforms.

Symbolic uses setitimer() in wall clock mode to support the low overhead, periodic updating of a global time tick counter. The rate currently in use is 100 Hz and that frequency is the same regardless of hardware or position complexity. A search thread only has to look at the counter and check it against the recorded value the last time a poll/trace/whatever happened. If N ticks have gone by since the last event, then it's time for another event. The setitimer() overhead is too low to be accurately measured.
If you adjust X to your searchspeed every iteration then nodecount is equivelent to time.

The problem is a bit more hidden. If you return from the searchfunction on a timeout (wether nodes or time) you need to hit timeout at each ply. But in between you might actually reenter the search for a different move.

Tony
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Problem with pipes

Post by bob »

Tony wrote:
bob wrote:
Tony wrote:The probleem does not seem to be the reading of the stop command, but responding to it. How fast does your engine stop after receiving it ?

Some people try to save time by only checking the stop condition at the top of the searchfunctions, wich slows down stopping seriously.
Tony
really should not hurt since things are recursive. This should see the stop condition at least once every node...
I "optimized" even more by not checking it in Quiescence. Not a good idea if you do check giving moves as well.

Checking it only every X nodes is another bad idea.

I don't see why that would hurt. My ratio is 10-20x qsearch to normal, which means this flag would still be tested every 20 nodes or so. I would agree that checking every N nodes could slow it down a bit, depending on how big N is. I do exactly that for the check to see if there is input, but the "abort_search" flag is global and testing it every node hardly hurts enough to matter. There is a cost, but not a huge one.


Tony
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Problem with pipes

Post by bob »

sje wrote:
Tony wrote:I "optimized" even more by not checking it in Quiescence. Not a good idea if you do check giving moves as well.

Checking it only every X nodes is another bad idea.
I say that all schemes that check based on node count are faulty because:

1) Excessive dependence on underlying processor speed,

2) Excessive dependence on a variable position processing time,

3) The much, much better alternative of setitimer() is available on Unix-like platforms.

Symbolic uses setitimer() in wall clock mode to support the low overhead, periodic updating of a global time tick counter. The rate currently in use is 100 Hz and that frequency is the same regardless of hardware or position complexity. A search thread only has to look at the counter and check it against the recorded value the last time a poll/trace/whatever happened. If N ticks have gone by since the last event, then it's time for another event. The setitimer() overhead is too low to be accurately measured.
I don't use that for portability reasons. But I get the same effect by constantly measuring Crafty's NPS and using that to pick a number N that tells me how often (how many nodes to search) before polling for input or checking to see if time has expired.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: Problem with pipes

Post by sje »

bob wrote:
sje wrote: I say that all schemes that check based on node count are faulty because:

1) Excessive dependence on underlying processor speed,

2) Excessive dependence on a variable position processing time,

3) The much, much better alternative of setitimer() is available on Unix-like platforms.

Symbolic uses setitimer() in wall clock mode to support the low overhead, periodic updating of a global time tick counter. The rate currently in use is 100 Hz and that frequency is the same regardless of hardware or position complexity. A search thread only has to look at the counter and check it against the recorded value the last time a poll/trace/whatever happened. If N ticks have gone by since the last event, then it's time for another event. The setitimer() overhead is too low to be accurately measured.
I don't use that for portability reasons. But I get the same effect by constantly measuring Crafty's NPS and using that to pick a number N that tells me how often (how many nodes to search) before polling for input or checking to see if time has expired.
The setitimer() routine is no less portable than the select() routine, the KbdPeek() routine, the PeekNamedPipe() routine, or any of several other routines referenced in Crafty's utility.c source.

The setitimer()/getitimer() system calls are as old as dirt because they were needed for kernel profiler support in the early days. The kernel manages three separate process timers no matter what the user wants; the setitimer() call just makes the the user program get informed about timer expiration(s).
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Problem with pipes

Post by bob »

sje wrote:
bob wrote:
sje wrote: I say that all schemes that check based on node count are faulty because:

1) Excessive dependence on underlying processor speed,

2) Excessive dependence on a variable position processing time,

3) The much, much better alternative of setitimer() is available on Unix-like platforms.

Symbolic uses setitimer() in wall clock mode to support the low overhead, periodic updating of a global time tick counter. The rate currently in use is 100 Hz and that frequency is the same regardless of hardware or position complexity. A search thread only has to look at the counter and check it against the recorded value the last time a poll/trace/whatever happened. If N ticks have gone by since the last event, then it's time for another event. The setitimer() overhead is too low to be accurately measured.
I don't use that for portability reasons. But I get the same effect by constantly measuring Crafty's NPS and using that to pick a number N that tells me how often (how many nodes to search) before polling for input or checking to see if time has expired.
The setitimer() routine is no less portable than the select() routine, the KbdPeek() routine, the PeekNamedPipe() routine, or any of several other routines referenced in Crafty's utility.c source.

The setitimer()/getitimer() system calls are as old as dirt because they were needed for kernel profiler support in the early days. The kernel manages three separate process timers no matter what the user wants; the setitimer() call just makes the the user program get informed about timer expiration(s).
I don't use select() on windows. peekNamedPipe() is used only in windows, select() is used only in *nix. But if you are going to use it, why not set the timer to the total search time you want to use and get just one signal to tell you when to terminate? It seemed, from your description, that you are not doing that. The only issue with setitimer() is that now you need to carefully check anything you do that can block, since a signal at the wrong time will interrupt a blocked system call and fail to complete it, You end up having to check errno to see if it is set to EINTR and if so, re-execute the call again. I once used this when I first started, but it isn't compatible with windows so I need to do it both ways and I didn't want that complexity although the idea of an alarm timer is still interesting.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: Problem with pipes

Post by sje »

bob wrote:
sje wrote:The setitimer()/getitimer() system calls are as old as dirt because they were needed for kernel profiler support in the early days. The kernel manages three separate process timers no matter what the user wants; the setitimer() call just makes the the user program get informed about timer expiration(s).
I don't use select() on windows. peekNamedPipe() is used only in windows, select() is used only in *nix. But if you are going to use it, why not set the timer to the total search time you want to use and get just one signal to tell you when to terminate? It seemed, from your description, that you are not doing that. The only issue with setitimer() is that now you need to carefully check anything you do that can block, since a signal at the wrong time will interrupt a blocked system call and fail to complete it, You end up having to check errno to see if it is set to EINTR and if so, re-execute the call again. I once used this when I first started, but it isn't compatible with windows so I need to do it both ways and I didn't want that complexity although the idea of an alarm timer is still interesting.
Symbolic gives not one but three time limits to a search: one checked at each node, one checked at each move at the root, and one checked at the start of each iteration. So a single alarm signal won't work.

Interruptable system calls (any call that can set errno to EINTR) are few and far between in a search thread and all can be easily bracketed with restart code. Even better, the signal management routines sigprocmask(), sigaction(), sigsuspend(), sigsetops(), and others can also handle the restart issue.

Also useful is the capability to catch signals like SIGINT in a similar way of catching a SIGALRM raised by setitimer(). I use a user-generated SIGINT to quickly stop the current search (if any) and return to the program command prompt instead of killing the application.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Problem with pipes

Post by bob »

sje wrote:
bob wrote:
sje wrote:The setitimer()/getitimer() system calls are as old as dirt because they were needed for kernel profiler support in the early days. The kernel manages three separate process timers no matter what the user wants; the setitimer() call just makes the the user program get informed about timer expiration(s).
I don't use select() on windows. peekNamedPipe() is used only in windows, select() is used only in *nix. But if you are going to use it, why not set the timer to the total search time you want to use and get just one signal to tell you when to terminate? It seemed, from your description, that you are not doing that. The only issue with setitimer() is that now you need to carefully check anything you do that can block, since a signal at the wrong time will interrupt a blocked system call and fail to complete it, You end up having to check errno to see if it is set to EINTR and if so, re-execute the call again. I once used this when I first started, but it isn't compatible with windows so I need to do it both ways and I didn't want that complexity although the idea of an alarm timer is still interesting.
Symbolic gives not one but three time limits to a search: one checked at each node, one checked at each move at the root, and one checked at the start of each iteration. So a single alarm signal won't work.

Interruptable system calls (any call that can set errno to EINTR) are few and far between in a search thread and all can be easily bracketed with restart code. Even better, the signal management routines sigprocmask(), sigaction(), sigsuspend(), sigsetops(), and others can also handle the restart issue.

Also useful is the capability to catch signals like SIGINT in a similar way of catching a SIGALRM raised by setitimer(). I use a user-generated SIGINT to quickly stop the current search (if any) and return to the program command prompt instead of killing the application.
I do essentially the same. I have an absolute time limit, a target time limit, and a quick-reply time limit. I used to use the interval timer approach until I started to run on windows, where such a facility wasn't available. I may well go back to that for linux and just have two ways to control the time.

What I would do using setitimer is this:

Set it for the normal time per move. Once that hits I set a flag "no more root moves" that says time is up, but until each currently "in-progress" root move is completed, don't stop. I would also now set the timer for the absolute limit so that if it goes off again, the search stops unconditionally. I would also ignore the "no more root moves" flag if the score has dropped significantly.

Would be cleaner than the check every so often approach I currently use... but not for windows.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: Problem with pipes

Post by sje »

bob wrote:What I would do using setitimer is this:

Set it for the normal time per move. Once that hits I set a flag "no more root moves" that says time is up, but until each currently "in-progress" root move is completed, don't stop. I would also now set the timer for the absolute limit so that if it goes off again, the search stops unconditionally. I would also ignore the "no more root moves" flag if the score has dropped significantly.

Would be cleaner than the check every so often approach I currently use... but not for windows.
Well, there must be some near-equivalent on Windows. After all, Cygwin supports setitimer()/getitimer() with exactly the same semantics as seen in OpenBSD and Linux.

My signal handler for setitimer's SIGALRM works because it is simple: Here it is:

Code: Select all


static void HandleSIGALRM(int value)
{
  if (!TensorTaskPtr) Die("HandleSIGALRM: null TensorTaskPtr");
  switch (value)
  {
    case SIGALRM:
      TensorTaskPtr->TickCount++; TensorTaskPtr->UsecCount += TickTimeLen;
      break;

    default:
      BadSwitch("HandleSIGALRM");
      break;
  };
}
All it's doing is incrementing a tick counter and the microsecond wall clock. It's up to the poll/check code in other threads to notice the changes. At least the other threads need only do a memory access instead of making a system call.

By the way, my experiments with 14 polling threads out of 16 threads total show that total poll/sleeplock overhead is less than 2.5% of one core. Each worker thread including the command processor and the I/O manager has an event queue, and the poll is for the number of waiting events. The approach is more flexible and maybe less hazardous than using tricky locking or pthread condition variables.