@@ -18,8 +18,8 @@
18## Idiomatic Haskell at the speed of C
18## Idiomatic Haskell at the speed of C
20Streamly is a Haskell library/framework providing basic building blocks
20Streamly is a Haskell library/framework providing basic building blocks
21−or combinators to perform fundamental as well as advanced programming
21+or combinators to build safe, scalable, modular and high performance
22−tasks with ease. The key features it provides are:
22+software systems. The key features it provides are:
24* Speed of C
24* Speed of C
25* Safety of Haskell
25* Safety of Haskell
@@ -43,10 +43,10 @@ repository](https://github.com/composewell/streamly-examples).
44## Modular Word Counting
44## Modular Word Counting
46−The `Fold` data type in streamly represents a consumer of stream. In
47−this example, we will use individual folds to count bytes, words and
46+In this example, we will use folds to count bytes, words and lines in
47+a file. A `Fold` is a composable stream consumer. We will see how
48−lines in a file. We will see how these individual folds can be composed
48+individual folds can be composed together to perform all the three
49−together to do all the three at once with the same performance.
49+counts at once with the same performance.
51See [WordCountModular.hs](https://github.com/composewell/streamly-examples/blob/master/examples/WordCountModular.hs)
51See [WordCountModular.hs](https://github.com/composewell/streamly-examples/blob/master/examples/WordCountModular.hs)
52for full working code including imports that we may have omitted
52for full working code including imports that we may have omitted
@@ -129,13 +129,10 @@ wc file =
129 & Stream.fold countAll -- IO (Int, Int, Int)
129 & Stream.fold countAll -- IO (Int, Int, Int)
132−### Observations
132+This example demonstrates the simple and concise API of streamly with
133−
133+excellent modularity. Experienced Haskellers would notice that we have
134−This example shows:
134+not used bytestrings, we simply use a stream of `Word8`, simplifying the
136−* Excellent modularity
137−* Simple and concise API and types
138−* No bytestrings required, just streams of Word8
140## Performance
137## Performance
@@ -165,7 +162,7 @@ sys 0m0.165s
166## Concurrent Word Counting
163## Concurrent Word Counting
168−To do word counting in parallel we divide the stream in chunks (arrays),
165+To count words in parallel we divide the stream into chunks (arrays),
169count properties in each chunk and then add all the counts. We use the
166count properties in each chunk and then add all the counts. We use the
170same code as above except that we use an array input instead of using a
167same code as above except that we use an array input instead of using a
171file input.
168file input.
@@ -185,10 +182,12 @@ countArray arr =
185 & Stream.decodeLatin1 -- SerialT IO Char
182 & Stream.decodeLatin1 -- SerialT IO Char
186 & Stream.foldl' count (Counts 0 0 0 True) -- IO Counts
183 & Stream.foldl' count (Counts 0 0 0 True) -- IO Counts
188−When combining the counts in two contiguous chunks, we would also need to
185+When combining the counts in two contiguous chunks, we would also need
189−know whether the first element of the next chunk was a space char or
186+to know whether the first element of the next chunk was a space char or
190−non-space to know whether the same word is continuing to the next chunk or
187+non-space to know whether the same word is continuing to the next chunk
191−if it is a new word. So add that too, giving (firstCharWasSpace, Counts).
188+or if it is a new word. `partialCounts` adds a `Bool` flag to `Counts`
189+returned by `countArray` to indicate whether the first character in the
193``` haskell
192``` haskell
194partialCounts :: Array Word8 -> IO (Bool, Counts)
193partialCounts :: Array Word8 -> IO (Bool, Counts)
@@ -224,7 +223,7 @@ wc file = do
224 & Stream.foldl' addCounts (False, Counts 0 0 0 True) -- IO (Bool, Counts)
223 & Stream.foldl' addCounts (False, Counts 0 0 0 True) -- IO (Bool, Counts)
227−Note that `Stream.aheadly` is the only difference in a concurrent and
226+Note that `Stream.fromAhead` is the only difference in a concurrent and
228non-concurrent program. If we remove that we still have a perfectly valid,
227non-concurrent program. If we remove that we still have a perfectly valid,
229well performing serial program. Notice, how succinctly and idiomatically
228well performing serial program. Notice, how succinctly and idiomatically
230we expressed the concurrent word counting problem.
229we expressed the concurrent word counting problem.
@@ -383,10 +382,6 @@ main = do
383 & Stream.mapM_ print
382 & Stream.mapM_ print
386−Notice, how succinctly and idiomatically we expressed the concurrent
387−directory tree traversal problem. Let us know if you can do it
388−significantly better with any other language or framework.
390## Rate Limiting
385## Rate Limiting
392For bounded concurrent streams, stream yield rate can be specified. For
387For bounded concurrent streams, stream yield rate can be specified. For
@@ -438,13 +433,21 @@ documentation](https://streamly.composewell.com) to know more.
439## Concurrency
434## Concurrency
441−Streamly uses lock-free synchronization for concurrent operations. It employs
436+Streamly uses lock-free synchronization for low overhead
442−auto-scaling of the degree of concurrency based on demand. For CPU bound tasks
437+concurrency. The number of tasks performed concurrently are
438+determined automatically based on the rate at which a consumer is
439+consuming the results. In other words, you do not need to manage
440+thread pools and decide how many threads to use for a particular
441+task. For CPU bound tasks it tries to keep the number of threads
443−it tries to keep the threads close to the number of CPUs available whereas for
442+close to the number of CPUs available whereas for IO bound tasks
444−IO bound tasks more threads can be utilized. Parallelism can be utilized with
443+more threads can be utilized.
445+Parallelism can be utilized with little overhead even if
445−little overhead even if the task size is very small. See [concurrency
446+the task size is very small, because it can automatically
447+switch to serial mode or batch multiple tasks on the
448+same CPU if that is more efficient. See [concurrency
446−benchmarks](https://github.com/composewell/concurrency-benchmarks) for detailed
449+benchmarks](https://github.com/composewell/concurrency-benchmarks) for
447−performance results and a comparison with the `async` package.
450+detailed performance results and a comparison with the `async` package.
449## Performance
452## Performance
@@ -452,41 +455,29 @@ As you have seen above in the word count example, streamly enables
452highly modular abstractions with the best possible performance (close to
455highly modular abstractions with the best possible performance (close to
453an equivalent C program).
456an equivalent C program).
455−For example, none of the existing Haskell libraries can work with byte
456−level streams with acceptable performance. Streamly provides excellent
457−performance even for byte level stream operations, it is made possible
458+Streamly provides excellent performance even for byte level stream
458−by employing efficient abstractions like `Unfold`s and terminating
459+operations, it is made possible by employing efficient abstractions like
459−`Fold`s. Byte level stream operations make programming simpler because
460+`Unfold`s and terminating `Fold`s. Byte level stream operations make
460−you do not have to deal with chunking and re-combining.
461+programming simpler because you do not have to deal with chunking and
462If you can write a program significantly faster in some other way or
464If you can write a program significantly faster in some other way or
463−with some other language, please let us know and we will improve. We
465+with some other language, please let us know and we will improve.
464−still have some areas to improve but we will surely get there for most
467## Benchmarks
467## Benchmarks
469−When it comes to stream operations, Haskell lists is the fastest
469+We measured several Haskell streaming implementations
470−existing implementation even though it supports only pure (not monadic)
470+on various micro-benchmarks. Please see [streaming
471−streams. Other streaming libraries are many times slower compared to
471+benchmarks](https://github.com/composewell/streaming-benchmarks) page
472−lists, so lists is our gold standard for performance comparison.
472+for detailed comparison of streamly with other streaming libraries.
474−In the following chart, positive y-axis displays how many times worse
475−is a list operation compared to the corresponding streamly operation,
476−negative y-axis shows the same where streamly is worse compared to
479−
481−This chart show only those operations where streamly and list
482−performance differs by more than 10%. For all other operations the
483−performance of both is more or less comparable.
485−See [streaming
474+These results show that streamly is the fastest effectful streaming
486−benchmarks](https://github.com/composewell/streaming-benchmarks) for
475+implementation on almost all the measured micro benchmarks. In many cases
487−detailed comparison with other Haskell streaming libraries.
476+it is up to 100x faster and in some cases even 1000x faster. In many
477+composite operation benchmarks streamly turns out to be significantly
478+faster than the Haskell lists implementation.
489−## Performance and Modularity
480+## Design Goals
491The goals of streamly from the very beginning have been, (1) simplicity
482The goals of streamly from the very beginning have been, (1) simplicity
492by unifying abstractions, (2) high performance. These are hard to
483by unifying abstractions, (2) high performance. These are hard to