I wrote a system which uses Storable::freeze() to serialize a data structure. It then uses IPC::Open2 and IO::Handle to spawn multiple child processes and write to them, reading back a status from each one.
When this runs, the children are causing huge CPU load (according to top), even when nothing is being read. I can't figure out why, probably due to my inexperience with IPC. I have RTFM'ed just about every IPC document I could get my hands on. There are multiple parents running at any given time and each parent will spawn any number of children. Right now, I'm experimenting with 10 children per parent. Some code for your review is below.
My question is: Is there something I'm missing in this code that would cause the CPUs to churn even when simply selecting and reading? Or is there something else I'm just not seeing? Snippets from the parent:and later in the code...my ($readHandle, $writeHandle) = (IO::Handle->new, IO::Handle->new +); my $pid = open2($readHandle, $writeHandle, @command); $readHandle->autoflush(1); $readHandle->blocking(0); $writeHandle->autoflush(1); $writeHandle->blocking(0);
The children that are spawned have a method to read the data from the parent and process it (modified a bit to remove business logic):## $messageForProcessing is a data structure stored as a hash refe +rence my $frozenMessage = freeze($messageForProcessing); print $writeHandle $frozenMessage . qq|\n| . $this->_messageDelimi +ter();
sub processMessages { my ($this) = @_; my $endTime = time + $ENV{'MAX_LIFETIME'}; select STDOUT; $|++; # make unbuffered my $select = IO::Select->new(); $select->add(\*STDIN); my $messageDelimiter = $this->_messageDelimiter(); my $delimiterMatch = qr{\n$messageDelimiter$}; my $frozenMessage; my $messageToProcess = {}; my $stopNow = 0; while(time < $endTime) { my $offset; my ($handle) = $select->can_read(5); my $previousRead; while( ($handle) && (my $bytes = $handle->sysread($frozenMessage, 8192, $offse +t)) ) { $offset += $bytes; ## Because Storable may put newlines in the frozen object, + ## delimit messages: my $searchableText = $previousRead . $frozenMessage; if($searchableText =~ /$delimiterMatch/) { $frozenMessage =~ s/$delimiterMatch//; $messageToProcess = thaw($frozenMessage); } $previousRead = $frozenMessage; if(%$messageToProcess) { ### DO MESSAGE PROCESSING HERE ## my $outputMessage; if($processor->hasErrors()) { my $errors = $processor->getErrors(); $outputMessage = qq|$$ ERROR $errors->[0]\n|; } else { my $messageID = $processor->lastMessageID(); $outputMessage = qq|$$ SUCCESS $messageID\n|; } ## Write status back to the parent print STDOUT $outputMessage; $offset = 0; undef($frozenMessage); undef($previousRead); undef(%$messageToProcess); } } } }
Process 22515 attached - interrupt to quit select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0 select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0 select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0 select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0 select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0 select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0 select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0 select(8, [0], NULL, NULL, {5, 0}) = 1 (in [0], left {5, 0}) read(0, "", 8192) = 0
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |