c-cube / tiny_httpd

Minimal HTTP server using good old threads + blocking IO, with a small request router.
https://c-cube.github.io/tiny_httpd
75 stars 11 forks source link

close stream after Response.output #55

Closed craff closed 1 year ago

craff commented 1 year ago

I submit a PR for issue #52

craff commented 1 year ago

Tests using wrk againt http_of_dir. Without the PR a lot of errors:

raffalli@oulala:~/Programming/tiny_httpd$ wrk -t5 -c100 -d1 http://localhost:8080/echo.sh
Running 1s test @ http://localhost:8080/echo.sh
  5 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.90ms    1.78ms  36.26ms   87.99%
    Req/Sec     4.23k     2.15k   10.04k    57.14%
  17692 requests in 1.10s, 2.25MB read
  Non-2xx or 3xx responses: 17622 <----- ERRORS

With the PR, no error

raffalli@oulala:~/Programming/tiny_httpd$ wrk -t5 -c100 -d1 http://localhost:8080/echo.sh
Running 1s test @ http://localhost:8080/echo.sh
  5 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    39.40ms   27.80ms 224.91ms   75.11%
    Req/Sec   522.94    288.25     2.31k    94.12%
  2657 requests in 1.10s, 541.99KB read
Requests/sec:   2407.38
Transfer/sec:    491.07KB
c-cube commented 1 year ago

thank you. good fix.

craff commented 1 year ago

The perf for simple_httpd using 5 domains + 1 for accept, on the same example are much better (reason below)

Almost same speed ;-) and 30 faster than tiny_httpd. I think the gain is because I had to write by own output buffer for the scheduler to work on write too. I think this scheduler could benefit to Tiny_httpd.

c-cube commented 1 year ago

not going to happen, since tiny_httpd will remain compatible with OCaml < 5.0.

craff commented 1 year ago

My output buffer is not related to ocaml 5. It is just a replacement for the out_channel of OCaml. But I am not sure it is the only reason for speed. I did one optimisation for large file/string:

I am not sure OCaml does this. Here is the code, if it interest you, I could backport it to Tiny_httpd (if it is indeed faster):

module Out_buf = struct
  type t = { fd : Simple_httpd_domain.client; b: Bytes.t
           ; mutable o : int; s : int }

  let create ?(buf_size=16* 4_096) fd =
    {fd; s=buf_size; o = 0; b=Bytes.make buf_size ' '}

  let push oc =
    assert(oc.o = oc.s);
    let w = Simple_httpd_domain.write oc.fd oc.b 0 oc.s in
    if w < oc.s then
      begin
        Bytes.blit oc.b oc.o oc.b 0 (oc.s - w);
        oc.o <- oc.s - w
      end
    else
      oc.o <- 0

  let flush oc =
    let n = ref 0 in
    while !n < oc.o do
      let w = Simple_httpd_domain.write oc.fd oc.b !n (oc.o - !n) in
      n := !n + w
    done;
    oc.o <- 0

  let close oc =
    flush oc; Simple_httpd_domain.close oc.fd

  let add_substring oc str offset len =
    if oc.o + len < oc.s then
      begin
        Bytes.blit_string str offset oc.b oc.o len;
        oc.o <- oc.o + len
      end
    else
      begin
        let start =
          if oc.o > 0 then
            begin
              let nb = oc.s - oc.o in
              Bytes.blit_string str offset oc.b oc.o nb;
              oc.o <- oc.s;
              flush oc;
              offset + nb
            end
          else
            offset
        in
        let n = ref start in
        while len - !n >= oc.s do
          let str = Bytes.unsafe_of_string str in
          let w = Simple_httpd_domain.write oc.fd str !n (len - !n) in
          n := !n + w
        done;
        Bytes.blit_string str !n oc.b 0 (len - !n);
        oc.o <- len - !n
      end

  let add_string oc str = add_substring oc str 0 (String.length str)

  let printf oc format =
    let cont s = add_string oc s in
    Printf.ksprintf cont format

  let add_bytes oc str =
    add_string oc (Bytes.unsafe_to_string str)

  let add_subbytes oc str offset len =
    add_substring oc (Bytes.unsafe_to_string str) offset len

  let add_char oc c =
    if oc.o >= oc.s then push oc;
    Bytes.set oc.b oc.o c;
    oc.o <- oc.o + 1

end
craff commented 1 year ago

The Simple_httpd_domain.write should be replaced by Unix.write (or Unix.single_write)

craff commented 1 year ago

I will also check what out_channel do ... I could propose a PR to ocaml too ?

craff commented 1 year ago

output_string of OCaml is in C !

craff commented 1 year ago

I did some check ... My proposed buffer are as fast as ocaml out_channel, but not faster. So no need to do anything.