A fast HTTP request/response parser for Common Lisp
github.com
github.com
[0]: https://github.com/fukamachi/quri
In what way? All of the benchmarks I'm seeing on that page indicate otherwise.
https://github.com/fukamachi/woo/commit/be4a0db688af05ee204e...
Probably libevent2 is the bottleneck and no way to make it faster without switching its backend (the cl-async's author is working on rewriting it with libuv now).
It sounds like there's someone with a lot of skill working to fix exactly this.
Now, do I have time to look into Lisp again...
Whatever the cause, I was under the impression that you essentially had to treat strings as lists of characters and that there really weren't any built-in functions to handle strings as strings. It seems that's wrong at least in modern day.
I've had very little problems operating on strings in Common Lisp (all I missed was starts-with and ends-with functions), could you give some concrete examples so that I might help you?
(This isn't meant as a personal jab, but too many people focus on silly things like microbenchmarks when choosing tools, and not on other more important things like support avenues, tooling, libraries, etc.)
The real story here is that people are working on a number of new libraries/modules/tools for doing web development in CL — the benchmarks (as you correctly point out) are a minor detail.
It's just not clear from the context we have. But, caution! Microbenchmarks should only be Microtrusted.
My grand takeaway from Aphyr's Jepsen series (http://aphyr.com/tags/jepsen) is that the more sophisticated software becomes in the never-ending quest for features and performance, the more likely the fundamentals underlying it have been overlooked, glossed over, or ignored completely.
As aroman said, "The real story here is that people are working on a number of new libraries/modules/tools for doing web development in CL".
But apgwoz's general point about how we evaluate software is spot on. In general, I think benchmarks and the like have a sort of "woo"ing effect on people (in the James Randi sense), and we should probably take a few moments to reflect on that before making any major architectural decisions. :)
Indeed. It's sad that as engineers (who theoretically espouse logic, reason, and evidence) we still end up making so many decisions based on herd behavior.
Leo Meyerovich did a study on the social factors that affect language adoption[0]. Vivek Haldar has a highlights the salient points[1]. More info at his site: [2]
[0]: http://lmeyerov.github.io/projects/socioplt/papers/oopsla201...
[1]: http://blog.vivekhaldar.com/post/56972066006/empirical-analy...
For my anecdotal experience, apart from JS libs (where style over substance is frequently the norm) I find a well-designed website is usually a sign the project cares about its users, and is more likely to provide good documentation & a space to ask questions.
The primary benefit and cost, depending on your point of view, of clojure is that it runs on the JVM. I don't see how that would be a strong argument for or against using this tooling for any problem domain unless you were already committed to or against the JVM for other reasons.
If you ask me the strengths of Clojure that it is built upon abstraction/interfaces (ie. conj instead of cons) and how it manages hygiene while retaining quasiquotes templates leveraging clj's namespaces. But then again you should probably ask a clojurian instead.
As a data point, I moved from Common Lisp to Clojure and never regretted it. But the reason I switched was not because I read something online, but because of Clojure's concurrency support, which means I can create concurrent software that is robust.
>Right. When I was profiling Wookie, I found URL parsing was the third bottleneck (the first is HTTP request parsing, and the second is libevent2).
By nature parsing strings is expensive (not sure why they designed HTTP in string format, binary would save a ton of power in processing). So imaging you are receiving a request, most of the time due to Network latency (client or server side), you don't get the full Request at once, most of the time you need to perform a couple of read(2) operations.
Now, what do you do on each read ?, did you manage full state ?, how many parsing rounds ?, how to catch the end of the protocol block , etc etc etc.
So answering your questions I would say "YES", the HTTP protocol in a Server "may be" a bottleneck if its not well implemented. In our HTTP Server project[0] we are working in a new parser to improve performance, so for real, this is a very important topic.
best
I would be more impressed if the callback table was not empty (and the callbacks actually did something, like increment an integer) and Lisp was still winning.
EDIT: I see others saying that SBCL is AOT, not JIT. If so I'd be very interested to look at the disassembly side-by-side and see an explanation for why SBCL is winning.
On the other hand there is nothing inherently stopping the optimiser from optimising the resulting code as well or better than GCC or any C compiler. Theoretically it could produce optimal machine code.
Note that C code or machine code would also have to perform input checks at runtime when doing http header parsing. A Common Lisp compiler wouldn't theoretically need to produce code performing any more runtime checks than these.
http://www.sbcl.org/manual/#Declarations-as-Assertions
4.2.1 Declarations as Assertions
The SBCL compiler treats type declarations differently from most other Lisp compilers. Under default compilation policy the compiler doesn’t blindly believe type declarations, but considers them assertions about the program that should be checked: all type declarations that have not been proven to always hold are asserted at runtime.
Remaining bugs in the compiler’s handling of types unfortunately provide some exceptions to this rule, see Implementation Limitations.
CLOS slot types form a notable exception. Types declared using the :type slot option in defclass are asserted if and only if the class was defined in safe code and the slot access location is in safe code as well. This laxness does not pose any internal consistency issues, as the CLOS slot types are not available for the type inferencer, nor do CLOS slot types provide any efficiency benefits.
There are three type checking policies available in SBCL, selectable via optimize declarations.
Full Type Checks
All declarations are considered assertions to be checked at runtime, and all type checks are precise. The default compilation policy provides full type checks.
Used when (or (>= safety 2) (>= safety speed 1)).
Weak Type Checks
Declared types may be simplified into faster to check supertypes: for example, (or (integer -17 -7) (integer 7 17)) is simplified into (integer -17 17).
Note: it is relatively easy to corrupt the heap when weak type checks are used if the program contains type-errors.
Used when (and (< safety 2) (< safety speed))
No Type Checks
All declarations are believed without assertions. Also disables argument count and array bounds checking.
Note: any type errors in code where type checks are not performed are liable to corrupt the heap.
Used when (= safety 0).
Although I agree that it is generally the case, you can read here[0] for an example where CL would gain an advantage over C compilers. Albeit in an implementation specific way.
[0]: http://www.pvk.ca/Blog/2014/08/16/how-to-define-new-intrinsi...
assert(parsed == strlen(buf));
He's calling strlen(buf) twice here. There's no guarantee that this is optimized at compile time. Usually len of buf is determined from read/recv system calls and for a fair comparison strlen should be removed here.
http://pubs.opengroup.org/onlinepubs/009695399/functions/ass...
http://git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c
As a guy not particularly proficient in C, that looks crazy to me. Although it does at least have decent comments, which I'd expect for such a mature project!
*** - READ from #<INPUT BUFFERED FILE-STREAM CHARACTER #P"benchmark.lisp" @1>: there is no package with name "SYNTAX"By the way, your error seems to be that the cl-syntax library is not being found, as opposed to a portability problem, so maybe you don't have Quicklisp installed and loaded?
Indeed I don't, I didn't know to install it since it wasn't listed as a dependency. Thanks for the info!
$ curl -O http://beta.quicklisp.org/quicklisp.lisp
$ sbcl --load quicklisp.lisp \
--eval '(quicklisp-quickstart:install)' \
--eval '(ql:add-to-init-file)' \
--quitIt's an immensely flexible language that lets you code in a variety of different styles, so you can end up with code spaghetti. But that doesn't mean that you have to, or even that it's the most likely thing to happen.
If you compare it to idiomatic Perl, where a lot of the community seem to get obsessed with the famous one-liners, Lisp seems a lot better to me. And if you follow a decent style guide like:
https://google-styleguide.googlecode.com/svn/trunk/lispguide...
Then there's no particular reason why you should end up with disaster code anymore than in any another language. I'd rather read a single page of Lisp, than the equivalent numerous pages of Java or some other verbose language.
It's basically what you're used to and for Lisp it does not take long to get used to.