Generating Power with Yield

1,143 views
1,004 views

Published on

My talk for the Nashville PHP Users Group August 2013 meeting.

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,143
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Generating Power with Yield

  1. 1. Generating Power with Yield/Jason Myers @jasonamyers
  2. 2. Yield, a modern language love story Originally Proposed in 1995, the yield keyword became official via the RFP on June 20th 2013 with PHP 5.5.Generators Facebook said a Hip, Hop and Don't Stop, and did their own yield generators in HipHop PHP
  3. 3. They are HEAVILY BASED off of Python, with a nod towards the Mozilla JS implementation and the await C# concept.
  4. 4. Iterator An object that lets us traverse a container
  5. 5. PHP Iterator Interface Iterator extends Traversable { /* Methods */ abstract public mixed current ( void ) abstract public scalar key ( void ) abstract public void next ( void ) abstract public void rewind ( void ) abstract public boolean valid ( void ) } For example checkout classArrayIterator
  6. 6. ArrayIterator Example $fruits = array( "apple" => "yummy", "orange" => "ah ya, nice", "grape" => "wow, I love it!", "plum" => "nah, not me" ); $obj = new ArrayObject( $fruits ); $it = $obj->getIterator(); echo "Iterating over: " . $obj->count() . " valuesn"; while( $it->valid() ) { echo $it->key() . "=" . $it->current() . "n"; $it->next(); } Iterating over: 4 values apple=yummy orange=ah ya, nice grape=wow, I love it! plum=nah, not me
  7. 7. Generator a special routine that can be used to control the iteration behavior of a loop, and yields the values one at a time
  8. 8. TL;DR A generator looks like a function but behaves like an iterator
  9. 9. Performant? range(0, 1000000) Uses over 100MB of RAM
  10. 10. Generator Version function xrange($start, $limit, $step = 1) { if ($start < $limit) { if ($step <= 0) { throw new LogicException('Step must be +ve'); } for ($i = $start; $i <= $limit; $i += $step) { yield $i; } } else { if ($step >= 0) { throw new LogicException('Step must be -ve'); } for ($i = $start; $i >= $limit; $i += $step) { yield $i; } } } uses less than 1KB!
  11. 11. TL; DR function xrange($min, $max) { for ($i = $min; $i < $max; $i++) { yield $i; } }
  12. 12. Sequences function collatz($val) { yield $val; while ($val != 1) { if ($val%2 == 0) { $val /= 2; } else { $val = 3*$val + 1; } yield $val; } } foreach (collatz(11) as $c) { echo $c," "; } 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
  13. 13. Y U Only LOOPin? It will work for any function that takes an Iterator or a Traversable as argument $arr = iterator_to_array(collatz(11));
  14. 14. Transformations function multiply_sequence($a, $fac) { foreach ($a as $val) { yield $val*$fac; } } function to_html_list($input) { foreach ($input as $val) { yield "<li>".$val."</li>"; } }
  15. 15. Chaining foreach (to_html_list(multiply_sequence(collatz(5),2)) as $val) { echo $val,"n"; } <li>10</li> <li>32</li> <li>16</li> <li>8</li> <li>4</li> <li>2</li>
  16. 16. Selections function select_pattern($input, $pattern) { foreach ($input as $val) { if (preg_match($pattern, $val)) { yield $val; } } }
  17. 17. Breath In function getLines($file) { $f = fopen($file, 'r'); if (!$f) { throw new Exception(); } while ($line = fgets($f)) { yield $line; } fclose($f); } foreach (getLines("someFile") as $line) { doSomethingWithLine($line); }
  18. 18. Breath Out function createLog($file) { $f = fopen($file, 'a'); while (true) { $line = yield; fwrite($f, $line); } } $log = createLog($file); $log->send("First"); $log->send("Second"); $log->send("Third");
  19. 19. Bro Remote Me! Fake the simultaneous processing of data
  20. 20. Green Threads threads that are scheduled by a virtual machine (VM/interperter?) instead of natively by the underlying operating system
  21. 21. function step1() { $f = fopen("file.txt", 'r'); while ($line = fgets($f)) { processLine($line); yield true; } }
  22. 22. function step2() { $f = fopen("file2.txt", 'r'); while ($line = fgets($f)) { processLine($line); yield true; } }
  23. 23. function step3() { $f = fsockopen("www.example.com", 80); stream_set_blocking($f, false); $headers = "GET / HTTP/1.1rn"; $headers .= "Host: www.example.comrn"; $headers .= "Connection: Closernrn"; fwrite($f, $headers); $body = ''; while (!feof($f)) { $body .= fread($f, 8192); yield true; } processBody($body); }
  24. 24. function runner(array $steps) { while (true) { foreach ($steps as $key => $step) { $step->next(); if (!$step->valid()) { unset($steps[$key]); } } if (empty($steps)) return; } } runner(array(step1(), step2(), step3()));
  25. 25. ZOMG... THERE BE DRAGONS! This relies on making sure we have no blocking IO
  26. 26. overREACTPHP much? event based, non-blocking IO - ReActPHP
  27. 27. One More Thing So if I can flip control, I can haz an Async?
  28. 28. class Buffer { protected $reads, $data; public function __construct() { $this->reads = new SplQueue(); $this->data = new SplQueue(); } public function read() { if( $this->data->isEmpty() ) { $deferred = new ReactPromiseDeferred(); $this->reads->enqueue($deferred->resolver()); return $deferred->promise(); } else { return ReactPromiseWhen::resolve($this->data->dequeue()); } } public function write($str) { if( $this->reads->isEmpty() ) { $this->data->enqueue($str); } else { $this->reads->dequeue()->resolve($str); } } }
  29. 29. function printer(Buffer $buffer) { while( true ) { $value = ( yield Util::async($buffer->read()) ); echo "Printer: ", $value, PHP_EOL; yield Util::async(nested_printer($buffer)); } }
  30. 30. function nested_printer(Buffer $buffer) { for( $i = 0; $i < 5; $i++ ) { // Yield a promise task and wait for the result - this is non-blocking $value = ( yield Util::async($buffer->read()) ); echo "Nested printer: ", $value, PHP_EOL; } }
  31. 31. $buffer = new Buffer(); $scheduler = new AsyncScheduler(); $scheduler->add(new AsyncTaskGeneratorTask(printer($buffer))); $i = 0; $scheduler->add(new AsyncTaskRecurringTask( function() use($buffer, &$i) { $buffer->write(++$i); } )); $scheduler->run(); Printer: 1 Nested printer: 2 Nested printer: 3 Nested printer: 4 Nested printer: 5 Nested printer: 6 Printer: 7 Nested printer: 8 Nested printer: 9 Nested printer: 10 Nested printer: 11 Nested printer: 12 ...
  32. 32. $loop = ReactEventLoopFactory::create(); $scheduler = new AsyncScheduler(); $scheduler->add(new AsyncTaskRecurringTask([$loop, 'tick'])); $scheduler->run();
  33. 33. Async Created by Matt Pryor, on Bitbucket
  34. 34. Thanks Huge thanks to Paul M. Jones and William Golden!
  35. 35. THE END @jasonamyers

×