In my example in the previous email, I accidentally used a very old version of echo. If you use the latest ("github.com/labstack/echo/v4"), then it's a lot faster than using simple string splitting, resulting in about 22,000 requests per second.
Concurrency Level: 100 Time taken for tests: 0.443 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 2160000 bytes HTML transferred: 430000 bytes Requests per second: 22565.77 [#/sec] (mean) Time per request: 4.431 [ms] (mean) Time per request: 0.044 [ms] (mean, across all concurrent requests) Transfer rate: 4759.97 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 0.5 2 9 Processing: 1 2 0.6 2 9 Waiting: 0 2 0.6 2 9 Total: 3 4 0.8 4 11 Percentage of the requests served within a certain time (ms) 50% 4 66% 4 75% 5 80% 5 90% 5 95% 5 98% 6 99% 6 100% 11 (longest request) On Mon, Jun 10, 2019 at 4:12 PM Marcin Romaszewicz <marc...@gmail.com> wrote: > One more followup. > > Here's an example using an HTTP router named Echo, which I use in > production. With proper HTTP parsing and validation, and no regular > expressions involved in routing, it still does about 14,000 requests per > second. I stubbed some of your stuff which doesn't affect the result. This > is a much better implementation than speaking HTTP yourself over sockets > and it's stupendously fast. > > Concurrency Level: 100 > Time taken for tests: 0.717 seconds > Complete requests: 10000 > Failed requests: 0 > Total transferred: 2160000 bytes > HTML transferred: 430000 bytes > Requests per second: 13940.29 [#/sec] (mean) > Time per request: 7.173 [ms] (mean) > Time per request: 0.072 [ms] (mean, across all concurrent requests) > Transfer rate: 2940.53 [Kbytes/sec] received > > Connection Times (ms) > min mean[+/-sd] median max > Connect: 0 3 1.2 3 8 > Processing: 1 4 1.2 4 9 > Waiting: 0 3 1.2 3 8 > Total: 2 7 1.3 7 13 > > Percentage of the requests served within a certain time (ms) > 50% 7 > 66% 7 > 75% 8 > 80% 8 > 90% 9 > 95% 10 > 98% 11 > 99% 11 > 100% 13 (longest request) > > > package main > > import ( > "flag" > "fmt" > "net/http" > "strconv" > > "github.com/labstack/echo" > ) > > const pixel = > "\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xFF\xFF\xFF\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B" > > func main() { > var port = flag.Int("port", 8080, "service port") > flag.Parse() > > autoProxy := fmt.Sprintf( > "function FindProxyForURL(url, host) { return \"PROXY %s:3128; > DIRECT\"; }", > "stubbed.host") > autoProxyBuf := []byte(autoProxy) > > e := echo.New() > > serveAutoProxy := func(c echo.Context) error { > response := c.Response() > response.Header().Add("Connection", "close") > return c.Blob(http.StatusOK, "application/octet-stream", autoProxyBuf) > } > e.GET("/proxy.pac", serveAutoProxy) > e.GET("/wpad.dat", serveAutoProxy) > > pixelBuf := []byte(pixel) > servePixel := func(c echo.Context) error { > response := c.Response() > response.Header().Add("Connection", "close") > response.Header().Add("ETag", "dbab") > response.Header().Add("Cache-Control", "public, max-age=31536000") > response.Header().Add("Content-length", strconv.Itoa(len(pixelBuf))) > return c.Blob(http.StatusOK, "image/gif", pixelBuf) > } > e.GET("*", servePixel) > > // Start server > e.Logger.Fatal(e.Start(fmt.Sprintf("0.0.0.0:%d", *port))) > } > > > > On Mon, Jun 10, 2019 at 4:07 PM Marcin Romaszewicz <marc...@gmail.com> > wrote: > >> I think the others were correct in pointing the finger at the RegEx >> engine in Go. It is quite slow. I hacked your inside loop which checks the >> request not to use regular expressions, and it's tons faster. You can't say >> that something can't be responsible for too much slowdown because it "1 >> line", since that one line has a lot of weight behind it. Using regular >> expressions, a benchmark showed 1782 requests per second. Using my simple >> hack, it's 18827 per second. Let's call it 10x faster. >> >> if strings.Contains(line, "HTTP") { >> parts := strings.Split(line, " ") >> req.Method = strings.ToUpper(strings.TrimSpace(parts[0])) >> req.URL = strings.ToUpper(strings.TrimSpace(parts[1])) >> req.Version = strings.ToUpper(strings.TrimSpace(parts[2])) >> continue >> } >> >> >> For this benchmark it behaves correctly, I realize this is fragile. I ran >> `ab -n 10000 -c 100...` to run the tests. >> >> Benchmarks using regular expressions: >> Concurrency Level: 100 >> Time taken for tests: 5.610 seconds >> Complete requests: 10000 >> Failed requests: 0 >> Total transferred: 1790000 bytes >> HTML transferred: 430000 bytes >> Requests per second: 1782.41 [#/sec] (mean) >> Time per request: 56.104 [ms] (mean) >> Time per request: 0.561 [ms] (mean, across all concurrent requests) >> Transfer rate: 311.57 [Kbytes/sec] received >> >> Connection Times (ms) >> min mean[+/-sd] median max >> Connect: 0 46 352.7 8 3557 >> Processing: 0 10 10.6 8 158 >> Waiting: 0 10 10.6 8 158 >> Total: 0 56 352.5 21 3565 >> >> Percentage of the requests served within a certain time (ms) >> 50% 21 >> 66% 24 >> 75% 24 >> 80% 24 >> 90% 25 >> 95% 26 >> 98% 164 >> 99% 3549 >> 100% 3565 (longest request) >> >> Benchmarks using my hack: >> >> Concurrency Level: 100 >> Time taken for tests: 0.531 seconds >> Complete requests: 10000 >> Failed requests: 0 >> Total transferred: 1790000 bytes >> HTML transferred: 430000 bytes >> Requests per second: 18827.39 [#/sec] (mean) >> Time per request: 5.311 [ms] (mean) >> Time per request: 0.053 [ms] (mean, across all concurrent requests) >> Transfer rate: 3291.12 [Kbytes/sec] received >> >> Connection Times (ms) >> min mean[+/-sd] median max >> Connect: 0 3 0.3 3 4 >> Processing: 1 3 0.4 3 5 >> Waiting: 0 3 0.4 3 5 >> Total: 3 5 0.5 5 8 >> >> Percentage of the requests served within a certain time (ms) >> 50% 5 >> 66% 5 >> 75% 5 >> 80% 5 >> 90% 6 >> 95% 6 >> 98% 7 >> 99% 7 >> 100% 8 (longest request) >> >> >> On Mon, Jun 10, 2019 at 2:28 PM Tong Sun <suntong...@gmail.com> wrote: >> >>> Just to clarify some facts. >>> >>> On Sun, Jun 9, 2019 at 11:09 AM 'Axel Wagner' wrote: >>> > >>> > As I've also mentioned: I don't think this test is meaningful. >>> > >>> > First, as it has been pointed out, your Perl program isn't actually a >>> web server. It only understands ridiculously simple requests and as such >>> violates the spec left and right. It's also super naive in how it treats >>> malformed input or actively malicious clients - all of which are handled by >>> the Go http package, so of course it's going to have some overhead. >>> >>> There is a second test well before your this post, which is a direct >>> translation of Perl code, that is now reading and writing directly to >>> a socket. Hanging on to the first test method and not referring to the >>> second test is not a very constructive way of discussion, let alone >>> using words like "ridiculously ...". >>> >>> > In its most generous form, you translate the programs faithfully to do >>> the same syscalls and implement the same logic - and at that point, you are >>> essentially benchmarking the Perl regular expression engine against the Go >>> regular expression engine, as that's the only thing the program really is >>> doing: Branching on a regexp-match. And the Perl RE-engine is famously >>> optimized and the Go RE-engine famously is not. In fact, you aren't even >>> benchmarking Perl against Go, you are benchmarking *C* against Go, as Perls >>> RE-engine is written in C, AFAIK. >>> >>> Over 90% of the code are NOT doing regular expression matching. >>> Focusing *only* on regular expression, not >90% of the rest, is not >>> very convincing but miss the elephant in the room, at least seems to >>> me. Many people have given valid inputs as where things might get >>> improved, including the one you are quoting. >>> >>> > Lastly, as I read the results I get with either net/http *or* the >>> naive regexp-parsing, I just… disagree with your conclusions. All the >>> numbers I've seen seem to imply that Go responded *on average* a lot faster >>> and *might* have higher variance and a slightly higher tail latency >>> (though, FTR, the measurements I got also suggest that the data is mostly >>> noise). And I'm struggling to find an application, where that would matter >>> a lot. Like, yeah, tail latency matters, but so does average latency. In >>> basically all applications I can think of, you a) want tail latencies to >>> not be catastrophic, especially when considering fan-out, but b) lower >>> averages are just as important anyway. So, I think you should, for a decent >>> test, formulate your criteria beforehand. Currently, there seems to be a >>> lot of reading in tea-leafs involved. >>> >>> Please don't get personal and emotional over technical discussions, >>> and please refrain from using terms like "ridiculously simple" or >>> "reading in tea-leafs" in future discussions. It is inappropriately >>> condescending, thus not the correct attitude of communication. It >>> does not align with the code of conduct of this mlist, and neither of >>> google's, I believe. >>> >>> If you have test results that contradict with mine, please show me >>> yours -- Let's talk the data, and not let the emotion get in the way >>> of technical discussion, and fact the fact, whatever it is. >>> >>> > Anyway, all of that being said: While I don't think the test you >>> devised allows the broad generalizations you are making, ultimately I don't >>> have any stakes in this (which is why I haven't responded further on the >>> blog post). If you like Perl and think it performs good enough or better >>> for your use case - then go ahead and use Perl. No one here will begrudge >>> you for it. >>> >>> As I've comment in the blog many times, I'm not trying to prove Perl >>> performs better than Go. On the contrary, I was trying to improve Perl >>> performs with Go, that's why the whole thing get started, as I have >>> replied to your comment in the blog:"that's why I was rewriting the >>> Perl code to Go". >>> >>> It is still the case, and I want to try out everyone's suggestion to >>> see how things can improve. >>> >>> Moreover, if you have taken a look at my second test, you will >>> understand my other goal is to make it clear "who and what to trust". >>> Because if you have taken a look at the httperf test result I posted >>> to this mlist before you reply, you may have realized that, of all the >>> performance testing tools I've used so far, all are suggesting Perl >>> performs better than Go, except for httperf. So maybe the performance >>> testing tools are biased toward Go, and I made it clear in my second >>> blog that I want to get to the bottom of it. >>> >>> > All of that being said >>> > >>> > IMO this is just not a meaningful test. Or its results are totally >>> unsurprising. >>> >>> Again, I was trying to improve Perl performs with Go. That >>> "ridiculously simple" Perl code is the foundation of the Debian dbab >>> package, and I was trying to improve it. >>> >>> It might be meaningless to you but it is perfectly meaningful to me. >>> Please don't be so judgmental. >>> >>> > On Sun, Jun 9, 2019 at 4:54 AM Justin Israel <justinisr...@gmail.com> >>> wrote: >>> >> >>> >> I'm wondering about a couple factors in this comparison that seem to >>> make a difference in my local test: >>> >> >>> >> I think perl sockets are write buffered. So would the equivalent be >>> to wrap the net.Conn in bufio.NewWriter(c) and flush before the Close? >>> >> Since this is a straigh-line test where both servers are not using >>> concurrent handling of connections (uncommon for a Go server even on 1 >>> core), would it not make sense to run the Go server with GOMAXPROCS=1? >>> >> >>> >> - Justin >>> >> >>> >> On Saturday, June 8, 2019 at 1:36:49 AM UTC+12, Tong Sun wrote: >>> >>> >>> >>> I had always believed that the web projects build with Go should be >>> much faster than Perl, since Go is a compiled language. >>> >>> >>> >>> However that belief was crushed brutally last night, when I did a >>> comparison -- the Go implementation is 8 times worse than the Perl! -- the >>> mean response time jumped from 6ms to 48ms. >>> >>> >>> >>> I know this is the simplest possible web server, but still, when it >>> comes to simple web servers like this, I have to say that Perl performs >>> much better than Go. >>> >>> >>> >>> I don't think there is much I can twist on the Go side, since it >>> can't be more simpler than that. However, I also believe it won't hurt to >>> ask and confirm. So, >>> >>> >>> >>> Have I missed anything? Is it possible for me to make my Go >>> implementation anywhere near the Perl's performance? >>> >>> >>> >>> Thanks >>> >>> >>> >>> >>> >> -- >>> >> You received this message because you are subscribed to the Google >>> Groups "golang-nuts" group. >>> >> To unsubscribe from this group and stop receiving emails from it, >>> send an email to golang-nuts+unsubscr...@googlegroups.com. >>> >> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/37a1ac7e-85ee-4775-b348-5673c41a162c%40googlegroups.com >>> . >>> >> For more options, visit https://groups.google.com/d/optout. >>> > >>> > -- >>> > You received this message because you are subscribed to a topic in the >>> Google Groups "golang-nuts" group. >>> > To unsubscribe from this topic, visit >>> https://groups.google.com/d/topic/golang-nuts/iH2Ck_hpCpI/unsubscribe. >>> > To unsubscribe from this group and all its topics, send an email to >>> golang-nuts+unsubscr...@googlegroups.com. >>> > To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/CAEkBMfF62D5v%2BRiGtAtzuH0wAHzCLqcwNUidC1Oe2KN9DfRv6Q%40mail.gmail.com >>> . >>> > For more options, visit https://groups.google.com/d/optout. >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "golang-nuts" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to golang-nuts+unsubscr...@googlegroups.com. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/CAMmz1OcefkrZN3_5R7%2BbjuwHjRx%2BLzttaiXmer0ytwmXUjxN_A%40mail.gmail.com >>> . >>> For more options, visit https://groups.google.com/d/optout. >>> >> -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CA%2Bv29LtxoqO6UJW-vntc9vDJFv%3DO%3D_izYD68nJKFN7u-oTx_2g%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.