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%2Bv29LvxbT31evyvgAwyDTOFw8WPzSiTFnW8fj_O8hGC9VTxcQ%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.