suave icon indicating copy to clipboard operation
suave copied to clipboard

Errors under load for LibUvServerFactory

Open hodzanassredin opened this issue 8 years ago • 39 comments

Trying to do load tests with Yandex.Tank rps_schedule=line(100,40000,2m) with DefaultTcpServerFactory everything is ok and no errors at all. Ubuntu vm can handle to 4k rps. But when I try to use LibUvServerFactory exceptions start to occur when load is more than 1400 rps and virtual users more than 31. benchs

I need to note that kestrel which also uses libuv also does'n work well, it doesnt show errors and works but it cant handle more than 200 rps on the same machine. So probably there is a problem in config. I installed libuv as described here https://docs.asp.net/en/latest/getting-started/installing-on-linux.html I tested both suave versions from nuget and from master branch.

let port = 80
let conf  = { defaultConfig with bindings = [ HttpBinding.mkSimple HTTP "0.0.0.0" port ];
                             tcpServerFactory = LibUvServerFactory()
                             bufferSize            = 2048
                             maxOps                = 100}


startWebServer conf (OK "Hello Suave")
[W] 2015-12-16T08:13:47.5331920Z: tcp request processing failed [Suave.Tcp.job] exn:
System.InvalidOperationException: Stack empty.
  at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x41a57190 + 0x00027> in <filename unknown>:0
  at System.Collections.Generic.Stack`1[T].Pop () <0x41a06040 + 0x0001f> in <filename unknown>:0
  at Suave.Sockets.BufferManager.PopBuffer (Microsoft.FSharp.Core.FSharpOption`1 context) <0x41a059a0 + 0x0005b> in <filename unknown>:0
  at [email protected] (Microsoft.FSharp.Core.Unit unitVar) <0x41a11830 + 0x00087> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@851[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1 args) <0x419f9510 + 0x0015a> in <filename unknown>:0

Unhandled Exception:
System.InvalidOperationException: Stack empty.
  at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x41a57190 + 0x00027> in <filename unknown>:0
  at System.Collections.Generic.Stack`1[T].Pop () <0x41a06040 + 0x0001f> in <filename unknown>:0
  at Suave.Sockets.BufferManager.PopBuffer (Microsoft.FSharp.Core.FSharpOption`1 context) <0x41a059a0 + 0x0005b> in <filename unknown>:0
  at [email protected] (Microsoft.FSharp.Core.Unit unitVar) <0x41a053f0 + 0x00217> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@851[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1 args) <0x419f9510 + 0x0015a> in <filename unknown>:0
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () <0x7fc7d44016d0 + 0x00029> in <filename unknown>:0
  at Microsoft.FSharp.Control.CancellationTokenOps+Start@1268-1.Invoke (System.Runtime.ExceptionServices.ExceptionDispatchInfo edi) <0x41a57900 + 0x0001e> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@851[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1 args) <0x419f9510 + 0x0040c> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+queueAsync@763[a].Invoke (Microsoft.FSharp.Core.Unit unitVar0) <0x419f7120 + 0x000e6> in <filename unknown>:0
  at <StartupCode$FSharp-Core>.$Control.loop@430-54 (Microsoft.FSharp.Control.Trampoline this, Microsoft.FSharp.Core.FSharpFunc`2 action) <0x419f70b0 + 0x00032> in <filename unknown>:0
  at Microsoft.FSharp.Control.Trampoline.ExecuteAction (Microsoft.FSharp.Core.FSharpFunc`2 firstAction) <0x419f6fd0 + 0x00067> in <filename unknown>:0
  at Microsoft.FSharp.Control.TrampolineHolder.Protect (Microsoft.FSharp.Core.FSharpFunc`2 firstAction) <0x419f6e50 + 0x0009b> in <filename unknown>:0
  at <StartupCode$FSharp-Core>[email protected] (System.Object state) <0x419f6ae0 + 0x0004b> in <filename unknown>:0
  at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context (System.Object state) <0x7fc7d44b5720 + 0x00048> in <filename unknown>:0
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, Boolean preserveSyncCtx) <0x7fc7d44af850 + 0x0016e> in <filename unknown>:0
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, Boolean preserveSyncCtx) <0x7fc7d44af820 + 0x00020> in <filename unknown>:0
  at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () <0x7fc7d44b56b0 + 0x00053> in <filename unknown>:0
  at System.Threading.ThreadPoolWorkQueue.Dispatch () <0x7fc7d44b3cc0 + 0x001d6> in <filename unknown>:0
  at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () <0x7fc7d44b5530 + 0x00008> in <filename unknown>:0

Unhandled Exception:
System.InvalidOperationException: Stack empty.
  at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x41a57190 + 0x00027> in <filename unknown>:0
  at System.Collections.Generic.Stack`1[T].Pop () <0x41a06040 + 0x0001f> in <filename unknown>:0
  at Suave.Sockets.BufferManager.PopBuffer (Microsoft.FSharp.Core.FSharpOption`1 context) <0x41a059a0 + 0x0005b> in <filename unknown>:0
  at [email protected] (Microsoft.FSharp.Core.Unit unitVar) <0x41a053f0 + 0x00217> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@851[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1 args) <0x419f9510 + 0x0015a> in <filename unknown>:0
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () <0x7fc7d44016d0 + 0x00029> in <filename unknown>:0
  at Microsoft.FSharp.Control.CancellationTokenOps+Start@1268-1.Invoke (System.Runtime.ExceptionServices.ExceptionDispatchInfo edi) <0x41a57900 + 0x0001e> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@851[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1 args) <0x419f9510 + 0x0040c> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+queueAsync@763[a].Invoke (Microsoft.FSharp.Core.Unit unitVar0) <0x419f7120 + 0x000e6> in <filename unknown>:0
  at <StartupCode$FSharp-Core>.$Control.loop@430-54 (Microsoft.FSharp.Control.Trampoline this, Microsoft.FSharp.Core.FSharpFunc`2 action) <0x419f70b0 + 0x00032> in <filename unknown>:0
  at Microsoft.FSharp.Control.Trampoline.ExecuteAction (Microsoft.FSharp.Core.FSharpFunc`2 firstAction) <0x419f6fd0 + 0x00067> in <filename unknown>:0
  at Microsoft.FSharp.Control.TrampolineHolder.Protect (Microsoft.FSharp.Core.FSharpFunc`2 firstAction) <0x419f6e50 + 0x0009b> in <filename unknown>:0
  at <StartupCode$FSharp-Core>[email protected] (System.Object state) <0x419f6ae0 + 0x0004b> in <filename unknown>:0
  at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context (System.Object state) <0x7fc7d44b5720 + 0x00048> in <filename unknown>:0
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, Boolean preserveSyncCtx) <0x7fc7d44af850 + 0x0016e> in <filename unknown>:0
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, Boolean preserveSyncCtx) <0x7fc7d44af820 + 0x00020> in <filename unknown>:0
  at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () <0x7fc7d44b56b0 + 0x00053> in <filename unknown>:0
  at System.Threading.ThreadPoolWorkQueue.Dispatch () <0x7fc7d44b3cc0 + 0x001d6> in <filename unknown>:0
  at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () <0x7fc7d44b5530 + 0x00008> in <filename unknown>:0

Unhandled Exception:
System.InvalidOperationException: Stack empty.
  at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x41a57190 + 0x00027> in <filename unknown>:0
  at System.Collections.Generic.Stack`1[T].Pop () <0x41a06040 + 0x0001f> in <filename unknown>:0
  at Suave.Sockets.BufferManager.PopBuffer (Microsoft.FSharp.Core.FSharpOption`1 context) <0x41a059a0 + 0x0005b> in <filename unknown>:0
  at [email protected] (Microsoft.FSharp.Core.Unit unitVar) <0x41a053f0 + 0x00217> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@851[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1 args) <0x419f9510 + 0x0015a> in <filename unknown>:0
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () <0x7fc7d44016d0 + 0x00029> in <filename unknown>:0
  at Microsoft.FSharp.Control.CancellationTokenOps+Start@1268-1.Invoke (System.Runtime.ExceptionServices.ExceptionDispatchInfo edi) <0x41a57900 + 0x0001e> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@851[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1 args) <0x419f9510 + 0x0040c> in <filename unknown>:0
  at Microsoft.FSharp.Control.AsyncBuilderImpl+queueAsync@763[a].Invoke (Microsoft.FSharp.Core.Unit unitVar0) <0x419f7120 + 0x000e6> in <filename unknown>:0
  at <StartupCode$FSharp-Core>.$Control.loop@430-54 (Microsoft.FSharp.Control.Trampoline this, Microsoft.FSharp.Core.FSharpFunc`2 action) <0x419f70b0 + 0x00032> in <filename unknown>:0
  at Microsoft.FSharp.Control.Trampoline.ExecuteAction (Microsoft.FSharp.Core.FSharpFunc`2 firstAction) <0x419f6fd0 + 0x00067> in <filename unknown>:0
  at Microsoft.FSharp.Control.TrampolineHolder.Protect (Microsoft.FSharp.Core.FSharpFunc`2 firstAction) <0x419f6e50 + 0x0009b> in <filename unknown>:0
  at <StartupCode$FSharp-Core>[email protected] (System.Object state) <0x419f6ae0 + 0x0004b> in <filename unknown>:0
  at System.Thr

hodzanassredin avatar Dec 16 '15 08:12 hodzanassredin

If you want to test code in your box just give me an url and I'll run tank against it.

hodzanassredin avatar Dec 16 '15 09:12 hodzanassredin

Thanks for doing this.

If you want to get rid of the errors just increase maxOps to a larger number.

On Wednesday, 16 December 2015, Hodza Nassredin [email protected] wrote:

If you want to test code in your box just give me an url and I'll run tank against it.

— Reply to this email directly or view it on GitHub https://github.com/SuaveIO/suave/issues/360#issuecomment-165044354.

Ademar Gonzalez +1-647-891-3606 http://ademar.name https://github.com/ademar

ademar avatar Dec 16 '15 09:12 ademar

Increased maxOps and yes no more erros but rps is verly low no more than 1-2 krps. Could you recommend some config values to play with?

hodzanassredin avatar Dec 16 '15 10:12 hodzanassredin

Something goes really weird with lubev. Process freezes and ctr-c doesnt work. I have to kill process. Is it ok to start suave from fsharpi?

hodzanassredin avatar Dec 16 '15 11:12 hodzanassredin

Tried to compile server but no difference. And one more thing then I open this url in browser it takes about 145 ms to get a response. But all other web servers usually do it in 65 ms. I don't know why. Unfortunately I have no time to dig deeper but there is something wrong with libuv integration.

hodzanassredin avatar Dec 16 '15 11:12 hodzanassredin

Tested h2o server with the same libuv version on the same machine . more than 10 k rps. No errors.

hodzanassredin avatar Dec 18 '15 13:12 hodzanassredin

Could you give a script to reproduce this? Like Yandex.Tank?

haf avatar Dec 18 '15 19:12 haf

Traffic source is ubuntu 14.04 vm 8 cores. Yandex tank installed as described here Installation and Tuning sections After installation created load.ini file and started as yandex-tank -c load.ini. You can find load.ini for yandex.tank and target vm (vm 8 cores) config files here You have to edit load ini file and enter your target vm ip address and host and url like address = 123.123.123.123 port=80 headers = [Host: example.com] urls=/

hodzanassredin avatar Dec 21 '15 08:12 hodzanassredin

Target vm conf files are a little bit dirty for historical reasons. So they could be a reason of bad suave's and kestrel's behaviour with libuv.

hodzanassredin avatar Dec 21 '15 08:12 hodzanassredin

Off topic. My IMHO thoughts(probably incorrect) how to speed up suave. During my work in an iot company(cars automation) we wrote a tcp/udp server. And unfortunately Task and Async is really bad in terms of performance and allocations. So simplest way to reduce costs is to use Hopac lib. Why allocations matter you can check this. Next problem is libuv. I don't think that this is the best way to achieve performance at all. Some interesting info could be found in Haskell: here and here. Also you can check how golang does it here (all files netpoll*)

hodzanassredin avatar Dec 21 '15 09:12 hodzanassredin

You can use wrk to profile, I too got a ton of errors like above:

wrk -t12 -c900 -d10s http://127.0.0.1:8086/

I actually get pretty poor performance on libuv and non libuv performance.

Last time I tried suave (non libuv) was 3x slower than a node js hello world and 25x slower than a rust iron hello world. In fact I tried clojure and python hello worlds and Suave was the slowest I could find.

7sharp9 avatar Jan 14 '16 18:01 7sharp9

So, what is the story with performance? Is async a source of performance issues in Suave?

Obviously I'm biased, but I think that, aside from familiarity, Hopac would be a very good fit for something like Suave.

polytypic avatar Jun 04 '16 12:06 polytypic

:)

Difficult to know what's going on here , very low in my radar right now.

I probably won't dedicate much more time to the libuv transport. Last time I checked the pure .net transport was performing better/faster. Marshaling impedance might be negating any performance gains libuv may have.

I don't think performance is a problem right now. Having said that it is totally possible in the future we bring Hopac into the mix.

On Jun 4, 2016, at 8:26 AM, Vesa Karvonen [email protected] wrote:

So, what is the story with performance? Is async a source of performance issues in Suave?

Obviously I'm biased, but I think that, aside from familiarity, Hopac would be a very good fit for something like Suave.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

ademar avatar Jun 04 '16 14:06 ademar

I write all our backend code with Hopac, so for me it makes sense to bring Hopac into Suave in the long run, if that can be done in an orderly fashion which benefits the community.

Right now, people I work with reliably report Hopac to have a high getting-started threshold, so until that has been addressed I would not agree to a full-out change of Suave into Hopac.

The Command-Backend-Events cycle we're building at qvitoo is dependent on Suave and I hope we'll have some improvements to Suave to show for it over the summer. Either as a library or as some patterns of async handling. Hopac would make it easier to deal with and those patterns we publish could help lowering the learning threshold.

Another way would be to produce a set of paid and free videos detailing how to use Hopac with Suave; a natural use-case for async clients in browser (and native) single page applications.

As you probably see, I'm also more focused on making the web development story for Suave fit really well with people's expectations (and hopes!). What do you think Vesa?

haf avatar Jun 04 '16 16:06 haf

I'm all for making Hopac more accessible.

I just spent quite a lot of time reordering, aligning and titling things in the generated reference manual with the intention of making it more accessible.

I've also been working on improving interop with other .Net async facilities. Note that the description doesn't currently describe all the changes. I plan to get rid of the Task, Async and IObservable "extensions" and turn those into functions in the Job and Alt modules making them (I hope) more accessible and implementing them with simpler (and sometimes faster) semantics.

I'm also thinking of spending some time this summer to eliminate the Scheduler abstraction and improve internal scheduling. The changes will also reduce the library interface area as everything Global and Scheduler will basically vanish.

polytypic avatar Jun 05 '16 08:06 polytypic

I'm all for helping to make the orderly switch to Hopac. Like @haf, I too generally use Hopac to back Suave, so I would be happy to assist in this.

neoeinstein avatar Jun 17 '16 14:06 neoeinstein

I went through and did the bare minimum of changing Async to Jobs in Suave (https://github.com/TheAngryByrd/suave/commit/d64a4639085161c4394d841e3b1254b028470af5). However when I did a benchmark (with the same test machine form https://github.com/Krzysztof-Cieslak/Suave.Kestrel/issues/1) I got a lower requests per second with Hopac than Async.

I did not go any further than this.

TheAngryByrd avatar Aug 04 '16 18:08 TheAngryByrd

I'd love to take a look and understand why Hopac performs poorly here.

So, what commands do I need to execute on OS X (or Windows haven't yet tried) to get a version of the benchmark running with async and jobs from scratch?

If I clone @TheAngryByrd 's Hopac branch and try the build instructions (bundle ...) I get errors. Here is the first (of many many) errors I see:

/Users/vesakarv/Projects/Fs/suave/src/Suave.OpenSSL/OpenSSL.fs(208,11): error FS0001: This expression was expected to have type    Async<'a>    but here has type    SocketOp<unit>

polytypic avatar Aug 04 '16 22:08 polytypic

I added a few comments to @TheAngryByrd Hopac branch and added a pair of new interop primitives to Hopac that might improve perf. I can see that the code (both with async and job) could be optimized significantly. I have not yet ran the benchmarks, because my quick attempts to build Suave fail. Building Suave master, bundle fails when trying to install nokogiri:

$ bundle
Fetching gem metadata from https://rubygems.org/
Fetching version metadata from https://rubygems.org/
Using rake 10.5.0
Using map 6.6.0
Using mini_portile2 2.1.0
Using pkg-config 1.1.7
Using semver2 3.4.2
Using bundler 1.12.5
Installing nokogiri 1.6.8 with native extensions

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /usr/local/lib/ruby/gems/2.3.0/gems/nokogiri-1.6.8/ext/nokogiri
/usr/local/opt/ruby/bin/ruby -r ./siteconf20160805-65611-ujj1cm.rb extconf.rb
Using pkg-config version 1.1.7
checking if the C compiler accepts ... yes
checking if the C compiler accepts -Wno-error=unused-command-line-argument-hard-error-in-future... no
Building nokogiri using packaged libraries.
Using mini_portile version 2.1.0
checking for iconv.h... yes
checking for gzdopen() in -lz... yes
checking for iconv using --with-opt-* flags... yes
************************************************************************
IMPORTANT NOTICE:

Building Nokogiri with a packaged version of libxml2-2.9.4.

Team Nokogiri will keep on doing their best to provide security
updates in a timely manner, but if this is a concern for you and want
to use the system library instead; abort this installation process and
reinstall nokogiri as follows:

    gem install nokogiri -- --use-system-libraries
        [--with-xml2-config=/path/to/xml2-config]
        [--with-xslt-config=/path/to/xslt-config]

If you are using Bundler, tell it to use the option:

    bundle config build.nokogiri --use-system-libraries
    bundle install

Note, however, that nokogiri is not fully compatible with arbitrary
versions of libxml2 provided by OS/package vendors.
************************************************************************
Extracting libxml2-2.9.4.tar.gz into tmp/x86_64-apple-darwin15.4.0/ports/libxml2/2.9.4... OK
Running 'configure' for libxml2 2.9.4... OK
Running 'compile' for libxml2 2.9.4... ERROR, review '/usr/local/lib/ruby/gems/2.3.0/gems/nokogiri-1.6.8/ext/nokogiri/tmp/x86_64-apple-darwin15.4.0/ports/libxml2/2.9.4/compile.log' to see what happened. Last lines are:
========================================================================
    unsigned short* in = (unsigned short*) inb;
                         ^~~~~~~~~~~~~~~~~~~~~
encoding.c:815:27: warning: cast from 'unsigned char *' to 'unsigned short *' increases required alignment from 1 to 2 [-Wcast-align]
    unsigned short* out = (unsigned short*) outb;
                          ^~~~~~~~~~~~~~~~~~~~~~
4 warnings generated.
  CC       error.lo
  CC       parserInternals.lo
  CC       parser.lo
  CC       tree.lo
  CC       hash.lo
  CC       list.lo
  CC       xmlIO.lo
xmlIO.c:1450:52: error: use of undeclared identifier 'LZMA_OK'
    ret =  (__libxml2_xzclose((xzFile) context) == LZMA_OK ) ? 0 : -1;
                                                   ^
1 error generated.
make[2]: *** [xmlIO.lo] Error 1
make[1]: *** [all-recursive] Error 1
make: *** [all] Error 2
========================================================================
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
    --with-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/usr/local/Cellar/ruby/2.3.1/bin/$(RUBY_BASE_NAME)
    --help
    --clean
    --use-system-libraries
    --enable-static
    --disable-static
    --with-zlib-dir
    --without-zlib-dir
    --with-zlib-include
    --without-zlib-include=${zlib-dir}/include
    --with-zlib-lib
    --without-zlib-lib=${zlib-dir}/lib
    --enable-cross-build
    --disable-cross-build
/usr/local/lib/ruby/gems/2.3.0/gems/mini_portile2-2.1.0/lib/mini_portile2/mini_portile.rb:366:in `block in execute': Failed to complete compile task (RuntimeError)
    from /usr/local/lib/ruby/gems/2.3.0/gems/mini_portile2-2.1.0/lib/mini_portile2/mini_portile.rb:337:in `chdir'
    from /usr/local/lib/ruby/gems/2.3.0/gems/mini_portile2-2.1.0/lib/mini_portile2/mini_portile.rb:337:in `execute'
    from /usr/local/lib/ruby/gems/2.3.0/gems/mini_portile2-2.1.0/lib/mini_portile2/mini_portile.rb:111:in `compile'
    from /usr/local/lib/ruby/gems/2.3.0/gems/mini_portile2-2.1.0/lib/mini_portile2/mini_portile.rb:150:in `cook'
    from extconf.rb:364:in `block (2 levels) in process_recipe'
    from extconf.rb:257:in `block in chdir_for_build'
    from extconf.rb:256:in `chdir'
    from extconf.rb:256:in `chdir_for_build'
    from extconf.rb:363:in `block in process_recipe'
    from extconf.rb:262:in `tap'
    from extconf.rb:262:in `process_recipe'
    from extconf.rb:555:in `<main>'

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /usr/local/lib/ruby/gems/2.3.0/extensions/x86_64-darwin-15/2.3.0/nokogiri-1.6.8/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /usr/local/lib/ruby/gems/2.3.0/gems/nokogiri-1.6.8 for inspection.
Results logged to /usr/local/lib/ruby/gems/2.3.0/extensions/x86_64-darwin-15/2.3.0/nokogiri-1.6.8/gem_make.out
An error occurred while installing nokogiri (1.6.8), and Bundler cannot continue.
Make sure that `gem install nokogiri -v '1.6.8'` succeeds before bundling.

I have not investigated this further.

polytypic avatar Aug 05 '16 06:08 polytypic

@polytypic Googling a bit suggests you may not have run xcode-select --install on your OS X machine, to install the Apple dev tools/XCode.

haf avatar Aug 05 '16 06:08 haf

@haf Thanks! After that and reinstalling libuv (and running into http://bugs.python.org/issue18378) I managed to run Suave master build to completion. So, then I need to get the Hopac branch built and run the benchmarks with it...

polytypic avatar Aug 05 '16 07:08 polytypic

@polytypic No worries. For future reference you can use https://github.com/haf/osx to provision your machine in a way that works work most languages :) – includes fixes to the above issues.

haf avatar Aug 05 '16 08:08 haf

I've now spent a little time looking at the changes and running the benchmarks. It seems that the hot paths still contain Async code. IOW, much of the leaf code or the routines that are called many times are still using Asyncs while some code in the middle uses Jobs. Converting/Transferring between Asyncs, Tasks and Jobs can be relatively expensive and now there are many unnecessary conversions/transfers per request. This is likely the reason that the modified Suave runs slower. To get good performance (using Hopac), one should use Task returning (system) ops directly from Jobs.

polytypic avatar Aug 05 '16 19:08 polytypic

So where are we with this? Anyone still working on it?

forki avatar Aug 26 '16 10:08 forki

Since this weekend I'm not traveling ill have a chance to take another crack at it.

TheAngryByrd avatar Aug 26 '16 12:08 TheAngryByrd

cool. so what's the goal?

forki avatar Aug 26 '16 12:08 forki

@forki To check where the hotspots are and whether we can bump the performance of Suave?

Here's one interesting article about serialisation by the way; https://rogeralsing.com/2016/08/16/wire-writing-one-of-the-fastest-net-serializers/

haf avatar Aug 26 '16 13:08 haf

Using dottrace, , the thing that takes the longest in Suave is TcpTransport.write method. (i know its windows only. I might try to use PrivateEye later on linux to see how they line up.)

suaveperf

I did these next benchmarks without a profiler attached.

This is the normal async version. It tends to use about 50% of the CPU on windows and about 75% on linux. Normal hello world example on my test machines:

Windows/ net461: 13000 req/s
Linux/ mono 4.4.1: 6200 req/s

I tried moving it over to Hopac using some of the recommendations from @polytypic (https://github.com/TheAngryByrd/suave/commit/d64a4639085161c4394d841e3b1254b028470af5). I started a new branch again just to be safe. I also tried to remove as much Async/Task interop as I could for that path. I'm sure I still missed some. This tends to use the max CPU it can on windows or linux. Moving to all over to Hopac :

Windows/ net461: 14000 req/s
Linux/ mono 4.4.1: 3900 req/s

I dug around for mono/SendAsync info, found this comment (it is 2 years old but might still apply).

Doing a terrible terrible hack

   member this.write (buf : ByteSegment) =
      async{
        if acceptArgs.AcceptSocket = null then
         return Choice2Of2 (ConnectionError "write error: acceptArgs.AcceptSocket = null") 
       else
         let foo = ResizeArray<ByteSegment>([|buf|])
         acceptArgs.AcceptSocket.Send(foo) |> ignore
         return Choice1Of2 ()
         //return! asyncDo acceptArgs.AcceptSocket.SendAsync (setBuffer buf) ignore writeArgs
      }
Windows/ net461: 14000 req/s
Linux/ mono 4.4.1: 9400 req/s

I'll try digging around in the LibUV implementation later today to see if theres anything there too.

TheAngryByrd avatar Aug 27 '16 15:08 TheAngryByrd

Since I know Nowin uses ScoketAsyncEventArgs. I quickly took it for a spin. I was able to get around 60k req/s. on my linux machine.

TheAngryByrd avatar Sep 05 '16 16:09 TheAngryByrd

???

forki avatar Sep 05 '16 16:09 forki