Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Streams, node.js and you by Ben Noordhuis

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

Streams, node.js and you by Ben Noordhuis

In this presentation shows Ben Noordhuis how working with streams will improve in node.js 0.10. Thereafter he gives a tour into the depths of V8 and shows how to do micro-optimisation by circumventing expensive division by divisor calls using add/sub/shift sequences.

Go to http://www.decodefriday.nl/presentation-on-firefox-os-and-node-js-insights/ to watch the full presentation.

Avatar for True Managed Hosting

True Managed Hosting

June 28, 2013

Other Decks in Programming

Transcript

  1. what are streams? like UNIX pipes $ c u t

    - f 1 | s o r t | u n i q - c | s o r t - n | t a c p r o c e s s . s t d i n . p i p e ( n e w C u t ( { f i e l d : 1 } ) ) . p i p e ( n e w S o r t ) . p i p e ( n e w U n i q ( { c o u n t : t r u e } ) . p i p e ( n e w S o r t ( { n u m e r i c : t r u e } ) . p i p e ( n e w R e v e r s e ) ;
  2. basic usage example / / m e s s a

    g e o f t h e d a y s e r v i c e ( t c p ) f u n c t i o n m o t d ( c o n n ) { r e q u i r e ( ' f s ' ) . c r e a t e R e a d S t r e a m ( ' / e t c / m o t d ' ) . p i p e ( c o n n ) ; } r e q u i r e ( ' n e t ' ) . c r e a t e S e r v e r ( m o t d ) . l i s t e n ( 8 0 0 0 ) ; $ n c 1 2 7 . 0 . 0 . 1 8 0 0 0 A l l s y s t e m s n o m i n a l .
  3. basic usage example / / m e s s a

    g e o f t h e d a y s e r v i c e ( h t t p ) f u n c t i o n m o t d ( r e q , r e s ) { r e q u i r e ( ' f s ' ) . c r e a t e R e a d S t r e a m ( ' / e t c / m o t d ' ) . p i p e ( r e s ) ; } r e q u i r e ( ' h t t p ' ) . c r e a t e S e r v e r ( m o t d ) . l i s t e n ( 8 0 0 0 ) ; $ c u r l - s h t t p : / / 1 2 7 . 0 . 0 . 1 : 8 0 0 0 / A l l s y s t e m s n o m i n a l .
  4. basic usage example / / c h a t s

    e r v i c e f o r n a r c i s s i s t s f u n c t i o n e c h o ( c o n n ) { c o n n . p i p e ( c o n n ) ; } r e q u i r e ( ' n e t ' ) . c r e a t e S e r v e r ( e c h o ) . l i s t e n ( 8 0 0 0 ) ; $ n c 1 2 7 . 0 . 0 . 1 8 0 0 0 p i n g ? p i n g ? ^ C
  5. basic usage example / / a n o n l

    i n e R E P L f u n c t i o n r e p l ( c o n n ) { r e q u i r e ( ' r e p l ' ) . s t a r t ( { i n p u t : c o n n , o u t p u t : c o n n } ) ; } r e q u i r e ( ' n e t ' ) . c r e a t e S e r v e r ( r e p l ) . l i s t e n ( 8 0 0 0 ) ; $ n c 1 2 7 . 0 . 0 . 1 8 0 0 0 > 1 + 1 2 > ^ C
  6. basic usage example / / e n c r y

    p t i o n s e r v i c e f u n c t i o n e n c r y p t ( r e q , r e s ) { v a r a r g s = r e q . u r l . s p l i t ( ' / ' ) . s l i c e ( 1 , 3 ) ; v a r a l g o = a r g s [ 0 ] , s e c r e t = a r g s [ 1 ] ; v a r c i p h e r = r e q u i r e ( ' c r y p t o ' ) . c r e a t e C i p h e r ( a l g o , s e c r e t ) ; r e q . p i p e ( c i p h e r ) . p i p e ( r e s ) ; } r e q u i r e ( ' h t t p ' ) . c r e a t e S e r v e r ( e n c r y p t ) . l i s t e n ( 8 0 0 0 ) ; $ c u r l - d ' P R I S M t h i s ! ' l o c a l h o s t : 8 0 0 0 / a e s 1 9 2 / s e c r e t | x x d - c 8 0 0 0 0 0 0 0 : 4 3 e 9 3 6 b d 2 b 4 6 c 5 5 3 C . 6 . + F . S 0 0 0 0 0 0 8 : d c a 5 b d 7 f e 6 a e a f 0 0 . . . . . . . .
  7. the bad old days streams in v0.8 (oldstable, maintenance) pro:

    easy to implement con: easy to implement wrong con: leaves much at implementer's discretion e.g. stream.pause() is optional v a r e v e n t s = r e q u i r e ( ' e v e n t s ' ) ; f u n c t i o n M y S t r e a m ( ) { t h i s . _ _ p r o t o _ _ = n e w e v e n t s . E v e n t E m i t t e r ; t h i s . w r i t e = f u n c t i o n ( d a t a ) { / * . . . * / } ; t h i s . e n d = f u n c t i o n ( ) { / * . . . * / } ; }
  8. happy days are here again streams in v0.10 (stable, current)

    pro: easy to implement con: some v0.8 code no longer works resume() force-starts stream / / b r o k e n , s t r e a m i s n o t s t a r t e d f u n c t i o n b r o k e n ( c o n n ) { c o n n . o n ( ' e n d ' , f u n c t i o n ( ) { c o n n . e n d ( ' B y e . ' ) ; } ) ; / / c o n n . r e s u m e ( ) ; } r e q u i r e ( ' n e t ' ) . c r e a t e S e r v e r ( b r o k e n ) . l i s t e n ( 8 0 0 0 ) ;
  9. Readable v a r R e a d a b

    l e = r e q u i r e ( ' s t r e a m ' ) . R e a d a b l e ; v a r w o r d s = r e q u i r e ( ' f s ' ) . r e a d F i l e S y n c ( ' / u s r / s h a r e / d i c t / w o r d s ' , ' u t f 8 ' ) . s p l i t ( ' \ n ' ) ; / / g e n e r a t e s ` n ` r a n d o m w o r d s f u n c t i o n R a n d o m W o r d S t r e a m ( n ) { t h i s . _ _ p r o t o _ _ = R e a d a b l e . c a l l ( t h i s ) ; t h i s . _ r e a d = f u n c t i o n ( s i z e ) { i f ( n - - = = = 0 ) r e t u r n t h i s . p u s h ( n u l l ) ; / / E O F v a r i n d e x = M a t h . r a n d o m ( ) * w o r d s . l e n g t h | 0 ; t h i s . p u s h ( w o r d s [ i n d e x ] ) ; } ; } ( n e w R a n d o m W o r d S t r e a m ( 8 ) ) . p i p e ( p r o c e s s . s t d o u t ) ;
  10. Readable only have to implement _read() size hint is optional

    push chunks to data sink can push to front of queue with unshift()
  11. Writable / / s p l i c e d

    a t a i n t o t w o t a r g e t s t r e a m s f u n c t i o n S p l i c e S t r e a m ( ) { t h i s . _ _ p r o t o _ _ = W r i t a b l e . c a l l ( t h i s ) ; t h i s . _ w r i t e = f u n c t i o n ( c h u n k , e n c o d i n g , d o n e ) { p r o c e s s . s t d o u t . w r i t e ( c h u n k ) ; p r o c e s s . s t d e r r . w r i t e ( c h u n k ) ; d o n e ( ) ; } ; } p r o c e s s . s t d i n . p i p e ( n e w S p l i c e S t r e a m ) ; / / s i l l y e x a m p l e , v e r b o s e w a y o f d o i n g : p r o c e s s . s t d i n . p i p e ( p r o c e s s . s t d o u t ) ; p r o c e s s . s t d i n . p i p e ( p r o c e s s . s t d e r r ) ;
  12. Writable / / n o t t o m e

    n t i o n w r o n g , d o e s n ' t h a n d l e b a c k p r e s s u r e / / _ w r i t e ( ) s h o u l d l o o k s o m e t h i n g l i k e t h i s t h i s . _ w r i t e = f u n c t i o n ( c h u n k , e n c o d i n g , d o n e ) { v a r n = 0 ; f u n c t i o n c b ( ) { i f ( + + n = = = 2 ) d o n e ( ) ; } p r o c e s s . s t d o u t . w r i t e ( c h u n k , c b ) ; p r o c e s s . s t d e r r . w r i t e ( c h u n k , c b ) ; } ;
  13. Writable only have to implement _write() encoding is always 'buffer'

    unless decodeStrings=false call done() when you're done
  14. Transformer v a r T r a n s f

    o r m = r e q u i r e ( ' s t r e a m ' ) . T r a n s f o r m ; f u n c t i o n C a p s L o c k S t r e a m ( ) { t h i s . _ _ p r o t o _ _ = T r a n s f o r m . c a l l ( t h i s ) ; t h i s . _ t r a n s f o r m = f u n c t i o n ( d a t a , e n c o d i n g , d o n e ) { f o r ( v a r i = 0 ; i < d a t a . l e n g t h ; i + + ) i f ( d a t a [ i ] > = 9 7 & & d a t a [ i ] < = 1 2 2 ) d a t a [ i ] & = ~ 3 2 ; t h i s . p u s h ( d a t a ) ; d o n e ( ) ; } ; } p r o c e s s . s t d i n . p i p e ( n e w C a p s L o c k S t r e a m ) . p i p e ( p r o c e s s . s t d o u t ) ;
  15. putting it all together / / g e n e

    r a t e r a n d o m w o r d s , u p p e r c a s e t h e m a n d p r i n t t o s t d { o u t , e r r } ( n e w R a n d o m W o r d S t r e a m ( 8 ) ) . p i p e ( n e w C a p s L o c k S t r e a m ) . p i p e ( n e w S p l i c e S t r e a m ) ; / / o k a y , n o t t o o e x c i t i n g b u t t h e r e ' s n o t e n o u g h r o o m o n t h e s e / / s l i d e s f o r a m o r e f u l l - f l e d g e d e x a m p l e
  16. object mode / / p r o d u c

    e a n d c o n s u m e o b j e c t s f u n c t i o n S o u r c e ( n ) { t h i s . _ _ p r o t o _ _ = R e a d a b l e . c a l l ( t h i s , { o b j e c t M o d e : t r u e } ) ; t h i s . _ c o u n t = 0 ; t h i s . _ r e a d = f u n c t i o n ( s i z e ) { i f ( + + t h i s . _ c o u n t = = = n ) t h i s . p u s h ( n u l l ) ; e l s e t h i s . p u s h ( { c o u n t : t h i s . _ c o u n t } ) ; } ; } f u n c t i o n S i n k ( ) { t h i s . _ _ p r o t o _ _ = W r i t a b l e . c a l l ( t h i s , { o b j e c t M o d e : t r u e } ) ; t h i s . _ w r i t e = f u n c t i o n ( o b j , _ , d o n e ) { c o n s o l e . l o g ( o b j ) ; d o n e ( ) ; } ; } ( n e w S o u r c e ( 4 ) ) . p i p e ( n e w S i n k ) ; $ n o d e e x a m p l e s / o b j e c t m o d e . j s { c o u n t : 1 } { c o u n t : 2 } { c o u n t : 3 } { c o u n t : 4 }
  17. recap Readable, Writable, Transformer easy to use, easy to implement

    mostly backwards compatible with v0.8 objectMode for those who want it
  18. / / d i v 3 . j s f

    u n c t i o n f ( x ) { r e t u r n M a t h . f l o o r ( x / 3 ) ; } f ( 1 0 ) ; % O p t i m i z e F u n c t i o n O n N e x t C a l l ( f ) ; f ( 1 0 ) ; D . D e b u g . d i s a s s e m b l e ( f ) ; $ o u t / x 6 4 . d e b u g / d 8 - - a l l o w - n a t i v e s - s y n t a x - - e x p o s e - d e b u g - a s = D \ d i v 3 . j s 2 > & 1 | g r e p - w C 2 i d i v l 0 x 2 9 a 7 c 2 8 4 5 d 9 b 5 9 b 9 0 3 0 0 0 0 0 0 m o v l r c x , 0 x 3 0 x 2 9 a 7 c 2 8 4 5 d a 0 6 4 9 9 c d q 0 x 2 9 a 7 c 2 8 4 5 d a 1 6 5 f 7 f 9 i d i v l r c x / / O H N O E S ! 0 x 2 9 a 7 c 2 8 4 5 d a 3 6 7 8 5 d 2 t e s t l r d x , r d x 0 x 2 9 a 7 c 2 8 4 5 d a 5 6 9 0 f 8 5 6 d 0 0 0 0 0 0 j n z 1 8 4 ( 0 x 2 9 a 7 c 2 8 4 5 e 1 8 )
  19. idiv is expensive! takes between 26 and 191 CPU cycles

    most instructions only take 1-3 cycles easily most expensive instruction in everyday use
  20. magic constants to the rescue! f u n c t

    i o n d i v 3 ( x ) { r e t u r n ( x * 0 x 5 5 5 6 ) > > 1 6 } $ o u t / x 6 4 . d e b u g / d 8 - - a l l o w - n a t i v e s - s y n t a x - - e x p o s e - d e b u g - a s = D \ d i v 3 . j s 2 > & 1 | g r e p - w C 2 i m u l l 0 x 3 f 9 d 9 a 4 4 4 b b a 2 6 0 f 8 5 1 e 0 0 0 0 0 0 j n z 6 2 ( 0 x 3 f 9 d 9 a 4 4 4 b d e ) 0 x 3 f 9 d 9 a 4 4 4 b c 0 3 2 4 8 c 1 e 8 2 0 R E X . W s h r q r a x , 3 2 0 x 3 f 9 d 9 a 4 4 4 b c 4 3 6 6 9 c 0 5 6 5 5 0 0 0 0 i m u l l r a x , r a x , 0 x 5 5 5 6 0 x 3 f 9 d 9 a 4 4 4 b c a 4 2 0 f 8 0 3 b 0 0 0 0 0 0 j o 1 0 7 ( 0 x 3 f 9 d 9 a 4 4 4 c 0 b ) 0 x 3 f 9 d 9 a 4 4 4 b d 0 4 8 c 1 f 8 1 0 s a r l r a x , 1 6 mul/imul is fast, 1-18 cycles pro: works for both signed and unsigned division con: doesn't work for large numbers only accurate for numbers between -32768 and 32767 d i v 3 ( 1 0 0 ) ; / / 3 3 d i v 3 ( - 1 0 0 ) ; / / - 3 4 M a t h . f l o o r ( 0 x 1 3 3 7 B A B E / 3 ) ; / / 1 0 7 4 7 3 1 3 0 ( 0 x 1 3 3 7 B A B E * 0 x 5 5 5 6 ) > > > 1 6 ; / / 6 2 9 0 5
  21. magic constants to the rescue! executed code conceptually looks like

    this / / l o g i c a l A N D w i t h s i g n e x t e n s i o n f u n c t i o n d i v 3 ( x ) { x * = 0 x 5 5 5 6 ; / / p s e u d o - c o d e w a r n i n g ! - 1 & 0 x F F F F F F F F = = - 1 i n J S , n o t 0 x F F F F F F F F x & = 0 x F F F F F F F F ; i f ( x & 0 x 8 0 0 0 0 0 0 0 ) x - = 0 x 1 0 0 0 0 0 0 0 0 ; / / s i g n e d n u m b e r r e t u r n x > > 1 6 ; } large numbers don't work because of nature of JS numbers binary arithmetic only has 32 bits of (signed!) precision. multiplying by e.g. 0x55556 and right-shifting 20 bits actually reduces range to -6143 - 6143.
  22. the shifty approach adds up f u n c t

    i o n d i v 3 ( x ) { v a r t = x > > 2 ; t + = t > > 2 ; t + = t > > 4 ; t + = t > > 8 ; t + = t > > 1 6 ; x - = t + t + t ; x + = ( x < < 3 ) + ( x < < 1 ) ; t + = x > > 5 ; r e t u r n t ; } pro: fast, only shifts, adds and one sub pro: works for both signed and unsigned division pro: works for large numbers (-2147483648 to 2147483647) con: only divides by 3
  23. / / d i v i d e b y

    1 0 u s i n g t h e s a m e a p p r o a c h f u n c t i o n d i v 1 0 ( x ) { v a r t ; x + = x > > 3 1 & 1 ; t = x > > 1 ; t + = t > > 1 ; t + = t > > 4 ; t + = t > > 8 ; t + = t > > 1 6 ; t > > = 3 ; x - = ( t < < 3 ) + ( t < < 1 ) ; t + = ( x + 6 ) > > 4 ; r e t u r n t ; }
  24. conclusion division by fixed divisor is expensive can always be

    reduced to add/sub/shift sequences for integers within range -2^31 to 2^31-1