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