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