51
Writing a fast HTTP parser Lisp Meetup #22 Eitaro Fukamachi

Writing a fast HTTP parser

Embed Size (px)

DESCRIPTION

At Lisp Meetup #22

Citation preview

Page 1: Writing a fast HTTP parser

Writing a fast HTTP parser

Lisp Meetup #22 Eitaro Fukamachi

Page 2: Writing a fast HTTP parser

Thank you for coming.

Page 3: Writing a fast HTTP parser

I’m Eitaro Fukamachi @nitro_idiot fukamachi

Page 4: Writing a fast HTTP parser

(and 'web-application-developer 'common-lisper)

Page 5: Writing a fast HTTP parser
Page 6: Writing a fast HTTP parser
Page 7: Writing a fast HTTP parser

We’re hiring! Tell @Rudolph_Miller.

Page 8: Writing a fast HTTP parser

fast-http

• HTTP request/response parser

• Written in portable Common Lisp

• Fast

• Chunked body parser

Page 9: Writing a fast HTTP parser

fast-http

Benchmarked with SBCL 1.2.5 / GCC v6.0.0

Page 10: Writing a fast HTTP parser

Let me tell why I had to write a fast HTTP parser.

Page 11: Writing a fast HTTP parser
Page 12: Writing a fast HTTP parser

Wookie is slower than Node.js

• Wookie is 2 times slower than Node.js

• Profiling result was saying “WOOKIE:READ-DATA” was pretty slow.

• It was only calling “http-parse”.

• “http-parse” which is an HTTP parser Wookie is using.

Page 13: Writing a fast HTTP parser

The bottleneck was HTTP parsing.

Page 14: Writing a fast HTTP parser

Wookie is slower than Node.js

• Node.js’s HTTP parse is “http-parser”.

• Written in C.

• General version of Nginx’s HTTP parser.

• Is it possible to beat it with Common Lisp?

Page 15: Writing a fast HTTP parser

Today, I’m talking what I did for writing a fast Common Lisp program.

Page 16: Writing a fast HTTP parser

5 important things

• Architecture

• Reducing memory allocation

• Choosing the right data types

• Benchmark & Profile

• Type declarations

Page 17: Writing a fast HTTP parser

5 important things

• Architecture

• Reducing memory allocation

• Choosing the right data types

• Benchmark & Profile

• Type declarations

Page 18: Writing a fast HTTP parser

A brief introduction of HTTP

Page 19: Writing a fast HTTP parser

HTTP request look like…

GET /media HTTP/1.1↵ Host: somewrite.jp↵ Connection: keep-alive↵ Accept: */*↵

Page 20: Writing a fast HTTP parser

HTTP request look like…

GET /media HTTP/1.1↵ Host: somewrite.jp↵ Connection: keep-alive↵ Accept: */*↵

First Line

Headers

Body (empty, in this case)

Page 21: Writing a fast HTTP parser

HTTP request look like…

GET /media HTTP/1.1↵ Host: somewrite.jp↵ Connection: keep-alive↵ Accept: */*↵

↵ CR + LF

CRLF * 2 at the end of headers

Page 22: Writing a fast HTTP parser

HTTP response look like…

HTTP/1.1 200 OK↵ Cache-Control: max-age=0↵ Content-Type: text/html↵ Date: Wed, 26 Nov 2014 04:52:55 GMT↵

↵ <html> …

Page 23: Writing a fast HTTP parser

HTTP response look like…

HTTP/1.1 200 OK↵ Cache-Control: max-age=0↵ Content-Type: text/html↵ Date: Wed, 26 Nov 2014 04:52:55 GMT↵

↵ <html> …

Status Line

Headers

Body

Page 24: Writing a fast HTTP parser

HTTP is…

• Text-based protocol. (not binary)

• Lines terminated with CRLF

• Very lenient.

• Ignore multiple spaces

• Allow continuous header values

Page 25: Writing a fast HTTP parser

And, there’s another difficulty.

Page 26: Writing a fast HTTP parser

HTTP messages are sent over a network.

Page 27: Writing a fast HTTP parser

Which means, we need to think about long & incomplete HTTP messages.

Page 28: Writing a fast HTTP parser

There’s 2 ways to resolve this problem.

Page 29: Writing a fast HTTP parser

1. Stateful (http-parser)

Page 30: Writing a fast HTTP parser

http-parser (used in Node.js)

• https://github.com/joyent/http-parser

• Written in C

• Ported from Nginx’s HTTP parser

• Written as Node.js’s HTTP parser

• Stateful

Page 31: Writing a fast HTTP parser

http-parser (used in Node.js)for (p=data; p != data + len; p++) { … switch (parser->state) { case s_dead: … case s_start_req_or_res: … case s_res_or_resp_H: … } }

Page 32: Writing a fast HTTP parser

http-parser (used in Node.js)for (p=data; p != data + len; p++) { … switch (parser->state) { case s_dead: … case s_start_req_or_res: … case s_res_or_resp_H: … } }

Process char by char

Do something for each state

Page 33: Writing a fast HTTP parser

2. Stateless (PicoHTTPParser)

Page 34: Writing a fast HTTP parser

PicoHTTPParser (used in H2O)

• https://github.com/h2o/picohttpparser

• Written in C

• Stateless

• Reparse when the data is incomplete

• Most HTTP request is small

Page 35: Writing a fast HTTP parser

And fast-http is…

Page 36: Writing a fast HTTP parser

fast-http is in the middle

• Not track state for every character

• Set state for every line

• It makes the program simple

• And easy to optimize

Page 37: Writing a fast HTTP parser

5 important things

• Architecture

• Reducing memory allocation

• Choosing the right data types

• Benchmark & Profile

• Type declarations

Page 38: Writing a fast HTTP parser

Memory allocation is slow

• (in general)

• Make sure not to allocate memory during processing

• cons, make-instance, make-array…

• subseq, append, copy-seq

Page 39: Writing a fast HTTP parser

5 important things

• Architecture

• Reducing memory allocation

• Choosing the right data types

• Benchmark & Profile

• Type declarations

Page 40: Writing a fast HTTP parser

Data types

• Wrong data type makes your program slow.

• List or Vector

• Hash Table or Structure or Class

Page 41: Writing a fast HTTP parser

5 important things

• Architecture

• Reducing memory allocation

• Choosing the right data types

• Benchmark & Profile

• Type declarations

Page 42: Writing a fast HTTP parser

Benchmark is quite important

• “Don’t guess, measure!”

• Check if your changes improve the performance.

• Benchmarking also keeps your motivation.

Page 43: Writing a fast HTTP parser

Profiling

• SBCL has builtin profiler

• (sb-profile:profile “FAST-HTTP” …)

• (sb-profile:report)

Page 44: Writing a fast HTTP parser

5 important things

• Architecture

• Reducing memory allocation

• Choosing the right data types

• Benchmark & Profile

• Type declarations

Page 45: Writing a fast HTTP parser

Type declaration

• Common Lisp has type declaration (optional)

• (declare (type <type> <variable symbol>))

• It’s a hint for your Lisp compiler

• (declare (optimize (speed 3) (safety 0)))

• It’s your wish to your Lisp compilerSee also: Cより高速なCommon Lispコードを書く

Page 46: Writing a fast HTTP parser

(safety 0)

• (safety 0) means “don’t check the type & array index in run-time”.

• Fast & unsafe (like C)

• Is fixnum enough?

• What do you do when someone passes a bignum to the function?

Page 47: Writing a fast HTTP parser

(safety 0)

• fast-http has 2 layers

• Low-level API

• (speed 3) (safety 0)

• High-level API (safer)

• Check the variable type

• (speed 3) (safety 2)

Page 48: Writing a fast HTTP parser

Attitude

Page 49: Writing a fast HTTP parser

Attitude

• Write carefully.

• It’s possible to beat C program

• (if the program is complicated enough)

• Don’t give up easily

• Safety is more important than speed

Page 50: Writing a fast HTTP parser

Thanks.

Page 51: Writing a fast HTTP parser

EITARO FUKAMACHI 8arrow.org @nitro_idiot fukamachi