forked from denoland/deno
/
http_bench.js
162 lines (139 loc) · 3.62 KB
/
http_bench.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// This is not a real HTTP server. We read blindly one time into 'requestBuf',
// then write this fixed 'responseBuf'. The point of this benchmark is to
// exercise the event loop in a simple yet semi-realistic way.
const OP_LISTEN = 1;
const OP_ACCEPT = 2;
const OP_READ = 3;
const OP_WRITE = 4;
const OP_CLOSE = 5;
function assert(cond) {
if (!cond) {
throw Error("assert");
}
}
const requestBuf = new Uint8Array(64 * 1024);
const responseBuf = new Uint8Array(
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n"
.split("")
.map(c => c.charCodeAt(0))
);
const promiseMap = new Map();
let nextPromiseId = 1;
function createResolvable() {
let methods;
const promise = new Promise((resolve, reject) => {
methods = { resolve, reject };
});
return Object.assign(promise, methods);
}
const scratch32 = new Int32Array(4);
const scratchBytes = new Uint8Array(
scratch32.buffer,
scratch32.byteOffset,
scratch32.byteLength
);
assert(scratchBytes.byteLength === 4 * 4);
// Toggle what method we send with. false = legacy.
// AFAICT This has no effect on performance.
const sendWithShared = true;
function send(promiseId, opId, arg, zeroCopy = null) {
scratch32[0] = promiseId;
scratch32[1] = opId;
scratch32[2] = arg;
scratch32[3] = -1;
if (sendWithShared) {
Deno._sharedQueue.push(scratchBytes);
libdeno.send(null, zeroCopy);
} else {
libdeno.send(scratchBytes, zeroCopy);
}
}
/** Returns Promise<number> */
function sendAsync(opId, arg, zeroCopyData) {
const promiseId = nextPromiseId++;
const p = createResolvable();
promiseMap.set(promiseId, p);
send(promiseId, opId, arg, zeroCopyData);
return p;
}
function recordFromBuf(buf) {
const buf32 = new Int32Array(buf.buffer, buf.byteOffset, buf.byteLength);
return {
promiseId: buf32[0],
opId: buf32[1],
arg: buf32[2],
result: buf32[3]
};
}
function recv() {
const buf = Deno._sharedQueue.shift();
if (!buf) {
return null;
}
return recordFromBuf(buf);
}
/** Returns u32 number */
function sendSync(opId, arg) {
send(0, opId, arg);
const record = recv();
assert(recv() == null);
return record.result;
}
function handleAsyncMsgFromRust(buf) {
const record = recordFromBuf(buf);
const { promiseId, result } = record;
const p = promiseMap.get(promiseId);
promiseMap.delete(promiseId);
p.resolve(result);
}
/** Listens on 0.0.0.0:4500, returns rid. */
function listen() {
return sendSync(OP_LISTEN, -1);
}
/** Accepts a connection, returns rid. */
async function accept(rid) {
return await sendAsync(OP_ACCEPT, rid);
}
/**
* Reads a packet from the rid, presumably an http request. data is ignored.
* Returns bytes read.
*/
async function read(rid, data) {
return await sendAsync(OP_READ, rid, data);
}
/** Writes a fixed HTTP response to the socket rid. Returns bytes written. */
async function write(rid, data) {
return await sendAsync(OP_WRITE, rid, data);
}
function close(rid) {
return sendSync(OP_CLOSE, rid);
}
async function serve(rid) {
while (true) {
const nread = await read(rid, requestBuf);
if (nread <= 0) {
break;
}
const nwritten = await write(rid, responseBuf);
if (nwritten < 0) {
break;
}
}
close(rid);
}
async function main() {
Deno.setAsyncHandler(handleAsyncMsgFromRust);
libdeno.print("http_bench.js start\n");
const listenerRid = listen();
libdeno.print(`listening http://127.0.0.1:4544/ rid = ${listenerRid}\n`);
while (true) {
const rid = await accept(listenerRid);
// libdeno.print(`accepted ${rid}`);
if (rid < 0) {
libdeno.print(`accept error ${rid}`);
return;
}
serve(rid);
}
}
main();