1############################################################################### 2## ---------------------------------------------------------------------------- 3## Parallel grep model similar to the native grep function. 4## 5############################################################################### 6 7package MCE::Grep; 8 9use strict; 10use warnings; 11 12no warnings qw( threads recursion uninitialized ); 13 14our $VERSION = '1.876'; 15 16## no critic (BuiltinFunctions::ProhibitStringyEval) 17## no critic (Subroutines::ProhibitSubroutinePrototypes) 18## no critic (TestingAndDebugging::ProhibitNoStrict) 19 20use Scalar::Util qw( looks_like_number weaken ); 21use MCE; 22 23our @CARP_NOT = qw( MCE ); 24 25my $_tid = $INC{'threads.pm'} ? threads->tid() : 0; 26 27sub CLONE { 28 $_tid = threads->tid() if $INC{'threads.pm'}; 29} 30 31############################################################################### 32## ---------------------------------------------------------------------------- 33## Import routine. 34## 35############################################################################### 36 37my ($_MCE, $_def, $_params, $_prev_c, $_tag) = ({}, {}, {}, {}, 'MCE::Grep'); 38 39sub import { 40 my ($_class, $_pkg) = (shift, caller); 41 42 my $_p = $_def->{$_pkg} = { 43 MAX_WORKERS => 'auto', 44 CHUNK_SIZE => 'auto', 45 }; 46 47 ## Import functions. 48 no strict 'refs'; no warnings 'redefine'; 49 50 *{ $_pkg.'::mce_grep_f' } = \&run_file; 51 *{ $_pkg.'::mce_grep_s' } = \&run_seq; 52 *{ $_pkg.'::mce_grep' } = \&run; 53 54 ## Process module arguments. 55 while ( my $_argument = shift ) { 56 my $_arg = lc $_argument; 57 58 $_p->{MAX_WORKERS} = shift, next if ( $_arg eq 'max_workers' ); 59 $_p->{CHUNK_SIZE} = shift, next if ( $_arg eq 'chunk_size' ); 60 $_p->{TMP_DIR} = shift, next if ( $_arg eq 'tmp_dir' ); 61 $_p->{FREEZE} = shift, next if ( $_arg eq 'freeze' ); 62 $_p->{THAW} = shift, next if ( $_arg eq 'thaw' ); 63 64 ## Sereal 3.015+, if available, is used automatically by MCE 1.8+. 65 if ( $_arg eq 'sereal' ) { 66 if ( shift eq '0' ) { 67 require Storable; 68 $_p->{FREEZE} = \&Storable::freeze; 69 $_p->{THAW} = \&Storable::thaw; 70 } 71 next; 72 } 73 74 _croak("Error: ($_argument) invalid module option"); 75 } 76 77 $_p->{MAX_WORKERS} = MCE::_parse_max_workers($_p->{MAX_WORKERS}); 78 79 MCE::_validate_number($_p->{MAX_WORKERS}, 'MAX_WORKERS', $_tag); 80 MCE::_validate_number($_p->{CHUNK_SIZE}, 'CHUNK_SIZE', $_tag) 81 unless ($_p->{CHUNK_SIZE} eq 'auto'); 82 83 return; 84} 85 86############################################################################### 87## ---------------------------------------------------------------------------- 88## Gather callback for storing by chunk_id => chunk_ref into a hash. 89## 90############################################################################### 91 92my ($_total_chunks, %_tmp); 93 94sub _gather { 95 96 my ($_chunk_id, $_data_ref) = @_; 97 98 $_tmp{$_chunk_id} = $_data_ref; 99 $_total_chunks++; 100 101 return; 102} 103 104############################################################################### 105## ---------------------------------------------------------------------------- 106## Init and finish routines. 107## 108############################################################################### 109 110sub init (@) { 111 112 shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); 113 my $_pkg = "$$.$_tid.".caller(); 114 115 $_params->{$_pkg} = (ref $_[0] eq 'HASH') ? shift : { @_ }; 116 117 _croak("$_tag: (HASH) not allowed as input by this MCE model") 118 if ( ref $_params->{$_pkg}{input_data} eq 'HASH' ); 119 120 @_ = (); 121 122 return; 123} 124 125sub finish (@) { 126 127 shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); 128 my $_pkg = (defined $_[0]) ? shift : "$$.$_tid.".caller(); 129 130 if ( $_pkg eq 'MCE' ) { 131 for my $_k ( keys %{ $_MCE } ) { MCE::Grep->finish($_k, 1); } 132 } 133 elsif ( $_MCE->{$_pkg} && $_MCE->{$_pkg}{_init_pid} eq "$$.$_tid" ) { 134 $_MCE->{$_pkg}->shutdown(@_) if $_MCE->{$_pkg}{_spawned}; 135 $_total_chunks = undef, undef %_tmp; 136 137 delete $_prev_c->{$_pkg}; 138 delete $_MCE->{$_pkg}; 139 } 140 141 @_ = (); 142 143 return; 144} 145 146############################################################################### 147## ---------------------------------------------------------------------------- 148## Parallel grep with MCE -- file. 149## 150############################################################################### 151 152sub run_file (&@) { 153 154 shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); 155 156 my $_code = shift; my $_file = shift; 157 my $_pid = "$$.$_tid.".caller(); 158 159 if (defined (my $_p = $_params->{$_pid})) { 160 delete $_p->{input_data} if (exists $_p->{input_data}); 161 delete $_p->{sequence} if (exists $_p->{sequence}); 162 } 163 else { 164 $_params->{$_pid} = {}; 165 } 166 167 if (defined $_file && ref $_file eq '' && $_file ne '') { 168 _croak("$_tag: ($_file) does not exist") unless (-e $_file); 169 _croak("$_tag: ($_file) is not readable") unless (-r $_file); 170 _croak("$_tag: ($_file) is not a plain file") unless (-f $_file); 171 $_params->{$_pid}{_file} = $_file; 172 } 173 elsif (ref $_file eq 'SCALAR' || ref($_file) =~ /^(?:GLOB|FileHandle|IO::)/) { 174 $_params->{$_pid}{_file} = $_file; 175 } 176 else { 177 _croak("$_tag: (file) is not specified or valid"); 178 } 179 180 @_ = (); 181 182 return run($_code); 183} 184 185############################################################################### 186## ---------------------------------------------------------------------------- 187## Parallel grep with MCE -- sequence. 188## 189############################################################################### 190 191sub run_seq (&@) { 192 193 shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); 194 195 my $_code = shift; 196 my $_pid = "$$.$_tid.".caller(); 197 198 if (defined (my $_p = $_params->{$_pid})) { 199 delete $_p->{input_data} if (exists $_p->{input_data}); 200 delete $_p->{_file} if (exists $_p->{_file}); 201 } 202 else { 203 $_params->{$_pid} = {}; 204 } 205 206 my ($_begin, $_end); 207 208 if (ref $_[0] eq 'HASH') { 209 $_begin = $_[0]->{begin}; $_end = $_[0]->{end}; 210 $_params->{$_pid}{sequence} = $_[0]; 211 } 212 elsif (ref $_[0] eq 'ARRAY') { 213 $_begin = $_[0]->[0]; $_end = $_[0]->[1]; 214 $_params->{$_pid}{sequence} = $_[0]; 215 } 216 elsif (ref $_[0] eq '' || ref($_[0]) =~ /^Math::/) { 217 $_begin = $_[0]; $_end = $_[1]; 218 $_params->{$_pid}{sequence} = [ @_ ]; 219 } 220 else { 221 _croak("$_tag: (sequence) is not specified or valid"); 222 } 223 224 _croak("$_tag: (begin) is not specified for sequence") 225 unless (defined $_begin); 226 _croak("$_tag: (end) is not specified for sequence") 227 unless (defined $_end); 228 229 $_params->{$_pid}{sequence_run} = undef; 230 231 @_ = (); 232 233 return run($_code); 234} 235 236############################################################################### 237## ---------------------------------------------------------------------------- 238## Parallel grep with MCE. 239## 240############################################################################### 241 242sub run (&@) { 243 244 shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); 245 246 my $_code = shift; $_total_chunks = 0; undef %_tmp; 247 my $_pkg = caller() eq 'MCE::Grep' ? caller(1) : caller(); 248 my $_pid = "$$.$_tid.$_pkg"; 249 250 my $_input_data; my $_max_workers = $_def->{$_pkg}{MAX_WORKERS}; 251 my $_r = ref $_[0]; 252 253 if (@_ == 1 && $_r =~ /^(?:ARRAY|HASH|SCALAR|CODE|GLOB|FileHandle|IO::)/) { 254 _croak("$_tag: (HASH) not allowed as input by this MCE model") 255 if $_r eq 'HASH'; 256 $_input_data = shift; 257 } 258 259 if (defined (my $_p = $_params->{$_pid})) { 260 $_max_workers = MCE::_parse_max_workers($_p->{max_workers}) 261 if (exists $_p->{max_workers}); 262 263 delete $_p->{sequence} if (defined $_input_data || scalar @_); 264 delete $_p->{user_func} if (exists $_p->{user_func}); 265 delete $_p->{user_tasks} if (exists $_p->{user_tasks}); 266 delete $_p->{use_slurpio} if (exists $_p->{use_slurpio}); 267 delete $_p->{bounds_only} if (exists $_p->{bounds_only}); 268 delete $_p->{gather} if (exists $_p->{gather}); 269 } 270 271 my $_chunk_size = MCE::_parse_chunk_size( 272 $_def->{$_pkg}{CHUNK_SIZE}, $_max_workers, $_params->{$_pid}, 273 $_input_data, scalar @_ 274 ); 275 276 if (defined (my $_p = $_params->{$_pid})) { 277 if (exists $_p->{_file}) { 278 $_input_data = delete $_p->{_file}; 279 } else { 280 $_input_data = $_p->{input_data} if exists $_p->{input_data}; 281 } 282 } 283 284 ## ------------------------------------------------------------------------- 285 286 MCE::_save_state($_MCE->{$_pid}); 287 288 if (!defined $_prev_c->{$_pid} || $_prev_c->{$_pid} != $_code) { 289 $_MCE->{$_pid}->shutdown() if (defined $_MCE->{$_pid}); 290 $_prev_c->{$_pid} = $_code; 291 292 my %_opts = ( 293 max_workers => $_max_workers, task_name => $_tag, 294 user_func => sub { 295 296 my ($_mce, $_chunk_ref, $_chunk_id) = @_; 297 my $_wantarray = $_mce->{user_args}[0]; 298 299 if ($_wantarray) { 300 my @_a; 301 302 if (ref $_chunk_ref eq 'SCALAR') { 303 local $/ = $_mce->{RS} if defined $_mce->{RS}; 304 open my $_MEM_FH, '<', $_chunk_ref; 305 binmode $_MEM_FH, ':raw'; 306 while (<$_MEM_FH>) { push (@_a, $_) if &{ $_code }; } 307 close $_MEM_FH; 308 weaken $_MEM_FH; 309 } 310 else { 311 if (ref $_chunk_ref) { 312 push @_a, grep { &{ $_code } } @{ $_chunk_ref }; 313 } else { 314 push @_a, grep { &{ $_code } } $_chunk_ref; 315 } 316 } 317 318 MCE->gather($_chunk_id, \@_a); 319 } 320 else { 321 my $_cnt = 0; 322 323 if (ref $_chunk_ref eq 'SCALAR') { 324 local $/ = $_mce->{RS} if defined $_mce->{RS}; 325 open my $_MEM_FH, '<', $_chunk_ref; 326 binmode $_MEM_FH, ':raw'; 327 while (<$_MEM_FH>) { $_cnt++ if &{ $_code }; } 328 close $_MEM_FH; 329 weaken $_MEM_FH; 330 } 331 else { 332 if (ref $_chunk_ref) { 333 $_cnt += grep { &{ $_code } } @{ $_chunk_ref }; 334 } else { 335 $_cnt += grep { &{ $_code } } $_chunk_ref; 336 } 337 } 338 339 MCE->gather($_cnt) if defined $_wantarray; 340 } 341 }, 342 ); 343 344 if (defined (my $_p = $_params->{$_pid})) { 345 for my $_k (keys %{ $_p }) { 346 next if ($_k eq 'sequence_run'); 347 next if ($_k eq 'input_data'); 348 next if ($_k eq 'chunk_size'); 349 350 _croak("$_tag: ($_k) is not a valid constructor argument") 351 unless (exists $MCE::_valid_fields_new{$_k}); 352 353 $_opts{$_k} = $_p->{$_k}; 354 } 355 } 356 357 for my $_k (qw/ tmp_dir freeze thaw /) { 358 $_opts{$_k} = $_def->{$_pkg}{uc($_k)} 359 if (exists $_def->{$_pkg}{uc($_k)} && !exists $_opts{$_k}); 360 } 361 362 $_MCE->{$_pid} = MCE->new(pkg => $_pkg, %_opts); 363 } 364 365 ## ------------------------------------------------------------------------- 366 367 my $_cnt = 0; my $_wantarray = wantarray; 368 369 $_MCE->{$_pid}{use_slurpio} = ($_chunk_size > &MCE::MAX_RECS_SIZE) ? 1 : 0; 370 $_MCE->{$_pid}{user_args} = [ $_wantarray ]; 371 372 $_MCE->{$_pid}{gather} = $_wantarray 373 ? \&_gather : sub { $_cnt += $_[0]; return; }; 374 375 if (defined $_input_data) { 376 @_ = (); 377 $_MCE->{$_pid}->process({ chunk_size => $_chunk_size }, $_input_data); 378 delete $_MCE->{$_pid}{input_data}; 379 } 380 elsif (scalar @_) { 381 $_MCE->{$_pid}->process({ chunk_size => $_chunk_size }, \@_); 382 delete $_MCE->{$_pid}{input_data}; 383 } 384 else { 385 if (defined $_params->{$_pid} && exists $_params->{$_pid}{sequence}) { 386 $_MCE->{$_pid}->run({ 387 chunk_size => $_chunk_size, 388 sequence => $_params->{$_pid}{sequence} 389 }, 0); 390 if (exists $_params->{$_pid}{sequence_run}) { 391 delete $_params->{$_pid}{sequence_run}; 392 delete $_params->{$_pid}{sequence}; 393 } 394 delete $_MCE->{$_pid}{sequence}; 395 } 396 } 397 398 MCE::_restore_state(); 399 400 if ($_wantarray) { 401 return map { @{ $_ } } delete @_tmp{ 1 .. $_total_chunks }; 402 } 403 elsif (defined $_wantarray) { 404 return $_cnt; 405 } 406 407 return; 408} 409 410############################################################################### 411## ---------------------------------------------------------------------------- 412## Private methods. 413## 414############################################################################### 415 416sub _croak { 417 418 goto &MCE::_croak; 419} 420 4211; 422 423__END__ 424 425############################################################################### 426## ---------------------------------------------------------------------------- 427## Module usage. 428## 429############################################################################### 430 431=head1 NAME 432 433MCE::Grep - Parallel grep model similar to the native grep function 434 435=head1 VERSION 436 437This document describes MCE::Grep version 1.876 438 439=head1 SYNOPSIS 440 441 ## Exports mce_grep, mce_grep_f, and mce_grep_s 442 use MCE::Grep; 443 444 ## Array or array_ref 445 my @a = mce_grep { $_ % 5 == 0 } 1..10000; 446 my @b = mce_grep { $_ % 5 == 0 } \@list; 447 448 ## Important; pass an array_ref for deeply input data 449 my @c = mce_grep { $_->[1] % 2 == 0 } [ [ 0, 1 ], [ 0, 2 ], ... ]; 450 my @d = mce_grep { $_->[1] % 2 == 0 } \@deeply_list; 451 452 ## File path, glob ref, IO::All::{ File, Pipe, STDIO } obj, or scalar ref 453 ## Workers read directly and not involve the manager process 454 my @e = mce_grep_f { /pattern/ } "/path/to/file"; # efficient 455 456 ## Involves the manager process, therefore slower 457 my @f = mce_grep_f { /pattern/ } $file_handle; 458 my @g = mce_grep_f { /pattern/ } $io; 459 my @h = mce_grep_f { /pattern/ } \$scalar; 460 461 ## Sequence of numbers (begin, end [, step, format]) 462 my @i = mce_grep_s { %_ * 3 == 0 } 1, 10000, 5; 463 my @j = mce_grep_s { %_ * 3 == 0 } [ 1, 10000, 5 ]; 464 465 my @k = mce_grep_s { %_ * 3 == 0 } { 466 begin => 1, end => 10000, step => 5, format => undef 467 }; 468 469=head1 DESCRIPTION 470 471This module provides a parallel grep implementation via Many-Core Engine. 472MCE incurs a small overhead due to passing of data. A fast code block will 473run faster natively. However, the overhead will likely diminish as the 474complexity increases for the code. 475 476 my @m1 = grep { $_ % 5 == 0 } 1..1000000; ## 0.065 secs 477 my @m2 = mce_grep { $_ % 5 == 0 } 1..1000000; ## 0.194 secs 478 479Chunking, enabled by default, greatly reduces the overhead behind the scene. 480The time for mce_grep below also includes the time for data exchanges between 481the manager and worker processes. More parallelization will be seen when the 482code incurs additional CPU time. 483 484 my @m1 = grep { /[2357][1468][9]/ } 1..1000000; ## 0.353 secs 485 my @m2 = mce_grep { /[2357][1468][9]/ } 1..1000000; ## 0.218 secs 486 487Even faster is mce_grep_s; useful when input data is a range of numbers. 488Workers generate sequences mathematically among themselves without any 489interaction from the manager process. Two arguments are required for 490mce_grep_s (begin, end). Step defaults to 1 if begin is smaller than end, 491otherwise -1. 492 493 my @m3 = mce_grep_s { /[2357][1468][9]/ } 1, 1000000; ## 0.165 secs 494 495Although this document is about MCE::Grep, the L<MCE::Stream> module can write 496results immediately without waiting for all chunks to complete. This is made 497possible by passing the reference to an array (in this case @m4 and @m5). 498 499 use MCE::Stream default_mode => 'grep'; 500 501 my @m4; mce_stream \@m4, sub { /[2357][1468][9]/ }, 1..1000000; 502 503 ## Completed in 0.203 secs. This is amazing considering the 504 ## overhead for passing data between the manager and workers. 505 506 my @m5; mce_stream_s \@m5, sub { /[2357][1468][9]/ }, 1, 1000000; 507 508 ## Completed in 0.120 secs. Like with mce_grep_s, specifying a 509 ## sequence specification turns out to be faster due to lesser 510 ## overhead for the manager process. 511 512A common scenario is grepping for pattern(s) inside a massive log file. 513Notice how parallelism increases as complexity increases for the pattern. 514Testing was done against a 300 MB file containing 250k lines. 515 516 use MCE::Grep; 517 518 my @m; open my $LOG, "<", "/path/to/log/file" or die "$!\n"; 519 520 @m = grep { /pattern/ } <$LOG>; ## 0.756 secs 521 @m = grep { /foobar|[2357][1468][9]/ } <$LOG>; ## 24.681 secs 522 523 ## Parallelism with mce_grep. This involves the manager process 524 ## due to processing a file handle. 525 526 @m = mce_grep { /pattern/ } <$LOG>; ## 0.997 secs 527 @m = mce_grep { /foobar|[2357][1468][9]/ } <$LOG>; ## 7.439 secs 528 529 ## Even faster with mce_grep_f. Workers access the file directly 530 ## with zero interaction from the manager process. 531 532 my $LOG = "/path/to/file"; 533 @m = mce_grep_f { /pattern/ } $LOG; ## 0.112 secs 534 @m = mce_grep_f { /foobar|[2357][1468][9]/ } $LOG; ## 6.840 secs 535 536=head1 PARSING HUGE FILES 537 538The MCE::Grep module lacks an optimization for quickly determining if a match 539is found from not knowing the pattern inside the code block. Use the following 540snippet as a template to achieve better performance. Also, take a look at 541examples/egrep.pl, included with the distribution. 542 543 use MCE::Loop; 544 545 MCE::Loop->init( 546 max_workers => 8, use_slurpio => 1 547 ); 548 549 my $pattern = 'karl'; 550 my $hugefile = 'very_huge.file'; 551 552 my @result = mce_loop_f { 553 my ($mce, $slurp_ref, $chunk_id) = @_; 554 555 ## Quickly determine if a match is found. 556 ## Process slurped chunk only if true. 557 558 if ($$slurp_ref =~ /$pattern/m) { 559 my @matches; 560 561 ## The following is fast on Unix. Performance degrades 562 ## drastically on Windows beyond 4 workers. 563 564 open my $MEM_FH, '<', $slurp_ref; 565 binmode $MEM_FH, ':raw'; 566 while (<$MEM_FH>) { push @matches, $_ if (/$pattern/); } 567 close $MEM_FH; 568 569 ## Therefore, use the following construct on Windows. 570 571 while ( $$slurp_ref =~ /([^\n]+\n)/mg ) { 572 my $line = $1; # save $1 to not lose the value 573 push @matches, $line if ($line =~ /$pattern/); 574 } 575 576 ## Gather matched lines. 577 578 MCE->gather(@matches); 579 } 580 581 } $hugefile; 582 583 print join('', @result); 584 585=head1 OVERRIDING DEFAULTS 586 587The following list options which may be overridden when loading the module. 588 589 use Sereal qw( encode_sereal decode_sereal ); 590 use CBOR::XS qw( encode_cbor decode_cbor ); 591 use JSON::XS qw( encode_json decode_json ); 592 593 use MCE::Grep 594 max_workers => 4, # Default 'auto' 595 chunk_size => 100, # Default 'auto' 596 tmp_dir => "/path/to/app/tmp", # $MCE::Signal::tmp_dir 597 freeze => \&encode_sereal, # \&Storable::freeze 598 thaw => \&decode_sereal # \&Storable::thaw 599 ; 600 601From MCE 1.8 onwards, Sereal 3.015+ is loaded automatically if available. 602Specify C<< Sereal => 0 >> to use Storable instead. 603 604 use MCE::Grep Sereal => 0; 605 606=head1 CUSTOMIZING MCE 607 608=over 3 609 610=item MCE::Grep->init ( options ) 611 612=item MCE::Grep::init { options } 613 614=back 615 616The init function accepts a hash of MCE options. The gather option, if 617specified, is ignored due to being used internally by the module. 618 619 use MCE::Grep; 620 621 MCE::Grep->init( 622 chunk_size => 1, max_workers => 4, 623 624 user_begin => sub { 625 print "## ", MCE->wid, " started\n"; 626 }, 627 628 user_end => sub { 629 print "## ", MCE->wid, " completed\n"; 630 } 631 ); 632 633 my @a = mce_grep { $_ % 5 == 0 } 1..100; 634 635 print "\n", "@a", "\n"; 636 637 -- Output 638 639 ## 2 started 640 ## 3 started 641 ## 1 started 642 ## 4 started 643 ## 3 completed 644 ## 4 completed 645 ## 1 completed 646 ## 2 completed 647 648 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 649 650=head1 API DOCUMENTATION 651 652=over 3 653 654=item MCE::Grep->run ( sub { code }, list ) 655 656=item mce_grep { code } list 657 658=back 659 660Input data may be defined using a list or an array reference. Unlike MCE::Loop, 661Flow, and Step, specifying a hash reference as input data isn't allowed. 662 663 ## Array or array_ref 664 my @a = mce_grep { /[2357]/ } 1..1000; 665 my @b = mce_grep { /[2357]/ } \@list; 666 667 ## Important; pass an array_ref for deeply input data 668 my @c = mce_grep { $_->[1] =~ /[2357]/ } [ [ 0, 1 ], [ 0, 2 ], ... ]; 669 my @d = mce_grep { $_->[1] =~ /[2357]/ } \@deeply_list; 670 671 ## Not supported 672 my @z = mce_grep { ... } \%hash; 673 674=over 3 675 676=item MCE::Grep->run_file ( sub { code }, file ) 677 678=item mce_grep_f { code } file 679 680=back 681 682The fastest of these is the /path/to/file. Workers communicate the next offset 683position among themselves with zero interaction by the manager process. 684 685C<IO::All> { File, Pipe, STDIO } is supported since MCE 1.845. 686 687 my @c = mce_grep_f { /pattern/ } "/path/to/file"; # faster 688 my @d = mce_grep_f { /pattern/ } $file_handle; 689 my @e = mce_grep_f { /pattern/ } $io; # IO::All 690 my @f = mce_grep_f { /pattern/ } \$scalar; 691 692=over 3 693 694=item MCE::Grep->run_seq ( sub { code }, $beg, $end [, $step, $fmt ] ) 695 696=item mce_grep_s { code } $beg, $end [, $step, $fmt ] 697 698=back 699 700Sequence may be defined as a list, an array reference, or a hash reference. 701The functions require both begin and end values to run. Step and format are 702optional. The format is passed to sprintf (% may be omitted below). 703 704 my ($beg, $end, $step, $fmt) = (10, 20, 0.1, "%4.1f"); 705 706 my @f = mce_grep_s { /[1234]\.[5678]/ } $beg, $end, $step, $fmt; 707 my @g = mce_grep_s { /[1234]\.[5678]/ } [ $beg, $end, $step, $fmt ]; 708 709 my @h = mce_grep_s { /[1234]\.[5678]/ } { 710 begin => $beg, end => $end, 711 step => $step, format => $fmt 712 }; 713 714=over 3 715 716=item MCE::Grep->run ( sub { code }, iterator ) 717 718=item mce_grep { code } iterator 719 720=back 721 722An iterator reference may be specified for input_data. Iterators are described 723under section "SYNTAX for INPUT_DATA" at L<MCE::Core>. 724 725 my @a = mce_grep { $_ % 3 == 0 } make_iterator(10, 30, 2); 726 727=head1 MANUAL SHUTDOWN 728 729=over 3 730 731=item MCE::Grep->finish 732 733=item MCE::Grep::finish 734 735=back 736 737Workers remain persistent as much as possible after running. Shutdown occurs 738automatically when the script terminates. Call finish when workers are no 739longer needed. 740 741 use MCE::Grep; 742 743 MCE::Grep->init( 744 chunk_size => 20, max_workers => 'auto' 745 ); 746 747 my @a = mce_grep { ... } 1..100; 748 749 MCE::Grep->finish; 750 751=head1 INDEX 752 753L<MCE|MCE>, L<MCE::Core> 754 755=head1 AUTHOR 756 757Mario E. Roy, S<E<lt>marioeroy AT gmail DOT comE<gt>> 758 759=cut 760 761