├── .gitignore ├── LICENSE ├── README.md ├── app.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Glen Mailer, All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-fib 2 | 3 | A little express server that gives you back whatever fibonacci number you ask for 4 | 5 | ## Coming here from Hacker News? 6 | 7 | Hello there! You've stumbled across a short script I wrote to see how effective a 8 | non-blocking implementation would be for the famous NodeJS cancer also known as 9 | calculating the fibonacci sequence recursively. 10 | 11 | They key differences from the original algorithm are that process.nextTick is used 12 | agressively (probably too agressively), so ensure that the main event loop is not 13 | blocked. In addition, memoisation is used which is shared across concurrent and 14 | subsequent requests without having to worry about locking. 15 | 16 | Obviously there are other algorithms to calculate the nth term of the fibonacci 17 | sequence, many of which will be more effective than this one (although I do like 18 | how easy it is to memoise this). Some people have already mentioned a few of these 19 | in the issues section. As this is a toy rather than a real project I'm unlikely 20 | to implement these improvements unless they sound particularly fun or I have some 21 | time to kill on a train. 22 | 23 | I would encourage anyone with suggestions of improvements to have a go at them 24 | yourself, especially if you're not familiar with NodeJS - you may even like it. 25 | I would also encourage any similar implementations in non-reactor frameworks as 26 | a comparison of the relative merits and disadvantages of event loops vs threads. 27 | 28 | ## Usage 29 | 30 | node app.js & 31 | curl localhost:3000/40 32 | 33 | Replace 40 with whatever fibonacci number you want 34 | 35 | ## Why? 36 | 37 | To point out that implementation of whatever you're doing is far more important 38 | than which framework you're using, or whether you're using threads or an event 39 | loop to handle concurrency. 40 | 41 | ## Is it any good? 42 | 43 | Yes. 44 | 45 | > ab -n 10000 -c 50 'http://127.0.0.1:3000/100' 46 | This is ApacheBench, Version 2.3 <$Revision: 655654 $> 47 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 48 | Licensed to The Apache Software Foundation, http://www.apache.org/ 49 | 50 | Benchmarking 127.0.0.1 (be patient) 51 | Completed 1000 requests 52 | Completed 2000 requests 53 | Completed 3000 requests 54 | Completed 4000 requests 55 | Completed 5000 requests 56 | Completed 6000 requests 57 | Completed 7000 requests 58 | Completed 8000 requests 59 | Completed 9000 requests 60 | Completed 10000 requests 61 | Finished 10000 requests 62 | 63 | 64 | Server Software: 65 | Server Hostname: 127.0.0.1 66 | Server Port: 3000 67 | 68 | Document Path: /100 69 | Document Length: 21 bytes 70 | 71 | Concurrency Level: 50 72 | Time taken for tests: 2.114 seconds 73 | Complete requests: 10000 74 | Failed requests: 0 75 | Write errors: 0 76 | Total transferred: 1420000 bytes 77 | HTML transferred: 210000 bytes 78 | Requests per second: 4731.49 [#/sec] (mean) 79 | Time per request: 10.568 [ms] (mean) 80 | Time per request: 0.211 [ms] (mean, across all concurrent requests) 81 | Transfer rate: 656.12 [Kbytes/sec] received 82 | 83 | Connection Times (ms) 84 | min mean[+/-sd] median max 85 | Connect: 0 0 0.3 0 5 86 | Processing: 2 10 3.0 10 28 87 | Waiting: 2 10 3.0 10 28 88 | Total: 3 10 3.0 10 28 89 | 90 | Percentage of the requests served within a certain time (ms) 91 | 50% 10 92 | 66% 11 93 | 75% 12 94 | 80% 13 95 | 90% 14 96 | 95% 15 97 | 98% 18 98 | 99% 20 99 | 100% 28 (longest request) 100 | 101 | 102 | ## Related 103 | 104 | Derivative project in python-twisted: https://github.com/zed/txfib -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var app = require('express').createServer(); 2 | 3 | var fibonacci = function(n, callback) { 4 | var inner = function(n1, n2, i) { 5 | if (i > n) { 6 | callback(null, n2); 7 | return; 8 | } 9 | var func = (i % 100) ? inner : inner_tick; 10 | func(n2, n1 + n2, i + 1); 11 | } 12 | var inner_tick = function(n1, n2, i) { 13 | process.nextTick(function() { inner(n1, n2, i); }); 14 | } 15 | if (n == 1 || n == 2) { 16 | callback(null, 1); 17 | } else { 18 | inner(1, 1, 3); 19 | } 20 | } 21 | 22 | /*** For reference, and to save you looking at the revision history, 23 | * this is the previous implementation. 24 | 25 | var async = require('async'); 26 | var fibonacci = function(n, callback) { 27 | if (n <= 2) { 28 | callback(null, 1); 29 | return; 30 | } 31 | async.series({ 32 | n2: function(next) { 33 | process.nextTick(function() { fibonacci(n - 2, next); }); 34 | }, 35 | n1: function(next) { 36 | process.nextTick(function() { fibonacci(n - 1, next); }); 37 | }, 38 | }, function(err, results) { 39 | callback(null, results.n1 + results.n2); 40 | }); 41 | } 42 | fibonacci = async.memoize(fibonacci); 43 | */ 44 | 45 | app.get('/:n', function(req, res) { 46 | fibonacci(req.params.n, function(err, number) { 47 | res.send(''+number); 48 | }); 49 | }); 50 | 51 | 52 | app.listen(3000); 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Glen Mailer ", 3 | "name": "fib", 4 | "description": "Use express to return fibonacci numbers", 5 | "version": "1.0.0", 6 | "repository": { 7 | "url": "" 8 | }, 9 | "main": "app", 10 | "engines": { 11 | "node": "~v0.4.12" 12 | }, 13 | "dependencies": {"express":"2.4.x"}, 14 | "devDependencies": {} 15 | } 16 | --------------------------------------------------------------------------------