Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] plaintext benchmark #9

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ install:
script:
- nimble install -y
- nimble test
- nimble benchmark
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the purpose of running benchmark on CI setup? neither travis nor appveyor provide stable hardware so numbers will be meaningless, and benchmarks tend to take a while so it will slow down every PR / build roundtrip..


5 changes: 4 additions & 1 deletion asyncdispatch2.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ task test, "Run all tests":
exec "nim c -r --gc:markAndSweep tests/testsync"
exec "nim c -r -d:release tests/testsync"


exec "nim c -r -d:useSysAssert -d:useGcAssert tests/testsoon"
exec "nim c -r tests/testsoon"
exec "nim c -r --gc:markAndSweep tests/testsoon"
Expand Down Expand Up @@ -60,3 +59,7 @@ task test, "Run all tests":
exec "nim c -r tests/testbugs"
exec "nim c -r --gc:markAndSweep tests/testbugs"
exec "nim c -r -d:release tests/testbugs"

task benchmark, "compile bench-bot":
exec "nim c -d:release tests/benchmark/bot"
exec "tests/benchmark/bot all"
13 changes: 13 additions & 0 deletions tests/benchmark/actix-raw/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "actix"
version = "0.7.1"

[dependencies]
futures = "0.1"
actix = "0.7"
actix-web = { version="0.7", default-features = false }

[profile.release]
lto = true
opt-level = 3
codegen-units = 1
11 changes: 11 additions & 0 deletions tests/benchmark/actix-raw/plaintext.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM rust:1.28-slim

ADD ./ /actix
WORKDIR /actix

RUN apt update -yqq && \
apt install -yqq libpq-dev
RUN cargo clean
RUN RUSTFLAGS="-C target-cpu=native" cargo build --release

CMD ./target/release/actix
67 changes: 67 additions & 0 deletions tests/benchmark/actix-raw/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
extern crate actix;
extern crate actix_web;
extern crate futures;

use actix::prelude::*;
use actix_web::server::{
self, HttpHandler, HttpHandlerTask, HttpServer, Request, Writer,
};
use actix_web::Error;
use futures::{Async, Poll};

const HTTPOK: &[u8] = b"HTTP/1.1 200 OK\r\n";
const HDR_SERVER: &[u8] = b"Server: Actix\r\n";
const HDR_CTPLAIN: &[u8] = b"Content-Type: text/plain";
const BODY: &[u8] = b"Hello, World!";

struct App {
}

impl HttpHandler for App {
type Task = Box<HttpHandlerTask>;

fn handle(&self, req: Request) -> Result<Box<HttpHandlerTask>, Request> {
{
let path = req.path();
match path.len() {
10 if path == "/plaintext" => return Ok(Box::new(Plaintext)),
_ => (),
}
}
Err(req)
}
}

struct Plaintext;

impl HttpHandlerTask for Plaintext {
fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error> {
{
let mut bytes = io.buffer();
bytes.reserve(196);
bytes.extend_from_slice(HTTPOK);
bytes.extend_from_slice(HDR_SERVER);
bytes.extend_from_slice(HDR_CTPLAIN);
server::write_content_length(13, &mut bytes);
}
io.set_date();
io.buffer().extend_from_slice(BODY);
Ok(Async::Ready(true))
}
}

fn main() {
let sys = System::new("techempower");

// start http server
HttpServer::new(move || {
vec![App { }]
}).backlog(8192)
.workers(1) // comment this to enable multithread mode
.bind("0.0.0.0:8080")
.unwrap()
.start();

println!("Started http server: 127.0.0.1:8080");
let _ = sys.run();
}
6 changes: 6 additions & 0 deletions tests/benchmark/asyncdispatch2/plaintext.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM statusteam/nim-base
RUN nimble install -y https://github.com/status-im/nim-asyncdispatch2
WORKDIR /server
COPY server.nim server.nim
RUN nim c -d:release server.nim
CMD ["./server"]
142 changes: 142 additions & 0 deletions tests/benchmark/asyncdispatch2/server.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import strutils, posix, deques, times, strformat
import asyncdispatch2

proc getFormattedTime*(): string =
result = format(getTime().inZone(utc()), "ddd, dd MMM yyyy hh:mm:ss 'GMT'")

type
IncomingCtx = ref object
buf: string
bufLen: int
resp: string
respLen: int

ServerCtx = ref object
ctxQueue: Deque[IncomingCtx]
serverTime: string

proc newIncomingCtx(readSize: int, writeSize: int): IncomingCtx =
result = IncomingCtx(
buf: newString(readSize),
bufLen: 0,
resp: newString(writeSize),
respLen: 0
)

proc newServerCtx*(readSize, writeSize: int, cap: int): ServerCtx =
new(result)
result.ctxQueue = initDeque[IncomingCtx](cap)
var ctxArray = newSeq[IncomingCtx](cap)
for i in 0 ..< cap:
ctxArray[i] = newIncomingCtx(readSize, writeSize)
GC_ref(ctxArray[i])
result.ctxQueue.addFirst(ctxArray[i])

proc getServerTime*(srv: ServerCtx): string =
result = srv.serverTime

proc updateServerTime*(srv: ServerCtx) =
srv.serverTime = getFormattedTime()

proc createCtx*(readSize, writeSize: int): IncomingCtx =
result = newIncomingCtx(readSize, writeSize)
GC_ref(result)

proc getIncomingCtx*(srv: ServerCtx, readSize, writeSize: int): IncomingCtx =
if srv.ctxQueue.len > 0:
return srv.ctxQueue.popFirst()
else:
return createCtx(readSize, writeSize)

proc freeCtx*(srv: ServerCtx, ctx: IncomingCtx) =
srv.ctxQueue.addLast(ctx)

proc resetBuffer(ctx: IncomingCtx) =
ctx.respLen = 0
ctx.bufLen = 0

proc sendMessage(ctx: IncomingCtx, body: string) =
while unlikely ctx.respLen + body.len > ctx.resp.len:
ctx.resp.setLen(ctx.resp.len + ctx.resp.len)
let ol = ctx.respLen
copyMem(addr ctx.resp[ol], unsafeAddr body[0], body.len)
ctx.respLen += body.len

proc readMessage*(ctx: IncomingCtx, st: StreamTransport): Future[int] {.async.} =
let rcvLimit =
block:
if unlikely(ctx.buf.len - ctx.bufLen == 0):
ctx.buf.setLen(ctx.buf.len + ctx.buf.len)
ctx.buf.len - ctx.bufLen

let rcv = await st.readOnce(addr ctx.buf[ctx.bufLen], rcvLimit)
ctx.bufLen += rcv
return rcv

proc makeResp(serverTime: string): string =
result = fmt("HTTP/1.1 200 OK\r\L" &
"Date: {serverTime}\r\l" &
"Server: asyncdispatch2\r\L" &
"Content-Type: text/plain\r\L" &
"Content-Length: 13\r\L\r\L" &
"Hello, World!")

proc handleIncoming(srv: ServerCtx, ctx: IncomingCtx, st: StreamTransport) {.async.} =
try:
while true:
let rcv = await ctx.readMessage(st)
if rcv == 0:
st.close()
srv.freeCtx(ctx)
return

var pos = 0
while (ctx.bufLen - pos) > 3:
if ctx.buf[pos] == '\r':
if ctx.buf[pos+1] == '\L' and
ctx.buf[pos+2] == '\r' and
ctx.buf[pos+3] == '\L':
ctx.sendMessage(makeResp(srv.serverTime))
inc pos

let wr = await st.write(ctx.resp[0].addr, ctx.respLen)
assert wr == ctx.respLen
ctx.resetBuffer()
except:
st.close()
srv.freeCtx(ctx)

proc handleConnection(ss: StreamServer, st: StreamTransport) {.async.} =
var srv = getUserData[ServerCtx](ss)
var ctx = srv.getIncomingCtx(1024, 1024)
ctx.resetBuffer()
asyncCheck handleIncoming(srv, ctx, st)

proc handleBreak(udata: pointer) =
var cdata = cast[ptr CompletionData](udata)
var svr = cast[StreamServer](cdata.udata)
echo "\nCTRL+C pressed, stopping server..."
svr.stop()
svr.close()

proc updateTime(arg: pointer = nil) {.gcsafe.} =
var svr = cast[ServerCtx](arg)
svr.updateServerTime()

proc serve(onAddress: string) =
let
ta = initTAddress(onAddress)
ctx = newServerCtx(1024, 1024, 128)
svr = createStreamServer(ta, handleConnection, {ReuseAddr}, backlog = 128, udata = cast[pointer](ctx))
when not defined(windows):
discard addSignal(SIGINT, handleBreak, udata = cast[pointer](svr))

ctx.updateServerTime()
addTimer(1000, updateTime, udata = cast[pointer](ctx))

svr.start()
echo "Server started at ", ta
waitFor svr.join()

when isMainModule:
serve("0.0.0.0:8080")
5 changes: 5 additions & 0 deletions tests/benchmark/asyncnet/plaintext.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM statusteam/nim-base
WORKDIR /server
COPY server.nim server.nim
RUN nim c -d:release server.nim
CMD ["./server"]