<p>There is a question on <a href="https://codereview.stackexchange.com/q/160489/133498" rel="nofollow">codereview.stackexchange</a></p>
<p>I have created a <a href="https://github.com/vardius/goserver" rel="nofollow">router/micro-framework</a>. I have split my code into multiple files by responsibility. All files are in the same directory. Would you recommend any way of improving it ? The most important one for a performance is a <a href="https://github.com/vardius/goserver/blob/master/tree.go" rel="nofollow">tree.go</a> file.</p>
<p>This is a <strong>benchmark results</strong> for <strong>4 cors CPU</strong>.</p>
<pre><code>$ go test -bench=. -cpu=4
BenchmarkStrict1-4 2000000 923 ns/op
BenchmarkStrict2-4 1000000 1138 ns/op
BenchmarkStrict3-4 1000000 1419 ns/op
BenchmarkStrict5-4 1000000 1767 ns/op
BenchmarkStrict10-4 500000 2356 ns/op
BenchmarkStrict100-4 100000 17943 ns/op
BenchmarkStrictParallel1-4 3000000 438 ns/op
BenchmarkStrictParallel2-4 2000000 501 ns/op
BenchmarkStrictParallel3-4 3000000 444 ns/op
BenchmarkStrictParallel5-4 2000000 639 ns/op
BenchmarkStrictParallel10-4 2000000 823 ns/op
BenchmarkStrictParallel100-4 200000 7039 ns/op
BenchmarkRegexp1-4 1000000 1840 ns/op
BenchmarkRegexp2-4 1000000 2899 ns/op
BenchmarkRegexp3-4 500000 3625 ns/op
BenchmarkRegexp5-4 300000 4777 ns/op
BenchmarkRegexp10-4 200000 9648 ns/op
BenchmarkRegexp100-4 10000 100741 ns/op
BenchmarkRegexpParallel1-4 2000000 937 ns/op
BenchmarkRegexpParallel2-4 1000000 1078 ns/op
BenchmarkRegexpParallel3-4 1000000 1692 ns/op
BenchmarkRegexpParallel5-4 500000 2414 ns/op
BenchmarkRegexpParallel10-4 500000 3906 ns/op
BenchmarkRegexpParallel100-4 50000 31972 ns/op
</code></pre>
<p>The output <code>BenchmarkStrict1-4 2000000 923 ns/op</code> means that the loop ran <code>2000000</code> times at a speed of <code>923 ns</code> per loop. Each benchmark name <code>BenchmarkStrict5-4</code> means that test used a <code>strict</code> or <code>regexp</code> route path for each node with a nested level <code>5</code>. Where 4 stands for CPU number.</p>
<p>For more details take a look <a href="https://github.com/vardius/goserver/blob/master/benchmark_test.go" rel="nofollow">benchmark_test.go</a></p>
<p>Do you know how can i make it faster ? I use mutex to make it thread safe for tree map access. But maybe there are some things along where i can improve my code/logic ?</p>
<p><strong><em>tree.go</em></strong></p>
<pre><code>package goserver
import (
"regexp"
"strings"
"sync"
)
type (
node struct {
path string
regexp *regexp.Regexp
route *route
parent *node
children tree
treeMu sync.RWMutex
}
tree map[string]*node
)
func (n *node) isRoot() bool {
return n.parent == nil
}
func (n *node) isLeaf() bool {
return len(n.children) == 0
}
func (n *node) regexpToString() string {
if n.regexp == nil {
return ""
}
return n.regexp.String()
}
func (n *node) setRegexp(exp string) {
reg, err := regexp.Compile(exp)
if err == nil {
n.regexp = reg
}
}
func (n *node) child(paths []string) (*node, map[string]string) {
if len(paths) > 0 && paths[0] != "" {
n.treeMu.RLock()
defer n.treeMu.RUnlock()
if child := n.children[paths[0]]; child != nil {
return child.child(paths[1:])
}
for path, child := range n.children {
if len(path) > 0 && path[:1] == ":" {
if child.regexp == nil || child.regexp.MatchString(paths[0]) {
node, params := child.child(paths[1:])
params[strings.Split(path, ":")[1]] = paths[0]
return node, params
}
}
}
} else if len(paths) == 0 {
return n, make(Params)
}
return nil, make(map[string]string)
}
func (n *node) addChild(paths []string) *node {
if len(paths) > 0 && paths[0] != "" {
n.treeMu.Lock()
defer n.treeMu.Unlock()
if n.children[paths[0]] == nil {
n.children[paths[0]] = newNode(n, paths[0])
}
return n.children[paths[0]].addChild(paths[1:])
}
return n
}
func (n *node) setRoute(r *route) {
n.route = r
}
func newNode(root *node, path string) *node {
n := &node{
path: path,
parent: root,
children: make(tree),
}
if parts := strings.Split(n.path, ":"); len(parts) == 3 {
n.setRegexp(parts[2])
}
return n
}
func newRoot(path string) *node {
return newNode(nil, path)
}
</code></pre>
<hr/>**评论:**<br/><br/>aboukirev: <pre><p>Keep in mind that many routers (Gorilla mux, for instance) don't bother with mutex on the router tree. The idea is that you construct the routes, build a tree, and then start serving, No modifications to the tree from that point on.</p>
<p>I can see one scenario where a user adds a resource and gives it an arbitrary alias, which might be useful to have in the router tree as a static (no parameters) route as it does not fit any predefined pattern. But that's a special case.</p>
<p>So one can get by without a mutex altogether.</p></pre>vardius-: <pre><p>This is true that on server run most of the routes are constructed and then served, but what if one specific app will need to create routes on the run ? dynamic routes of some kind that depends one a values from database or any other external source</p></pre>daveddev: <pre><p>Consider placing that logic within handlers.</p>
<ul>
<li>/api/something (static)</li>
<li>/api/another (static)</li>
<li>/blog/{all requests sent to special handler} (dynamic)</li>
</ul>
<p>This allows you to keep synchronization primitives closely associated with the need they fulfill.</p></pre>vardius-: <pre><p>i mean about case scenario where the route does not exist yet, like:</p>
<ul>
<li>/{username}/{user_property}</li>
</ul>
<p>and needs to be added when user is created. doesn't this or other more complex example needs thread safety ?</p></pre>daveddev: <pre><p>It appears that you're confusing dynamically added routes, and routes with parameters. At the moment, I'm unsure how to help bring clarity to the matter (I'm quite tired). If something comes to mind, I'll edit this post.</p>
<p>Edited...</p>
<p>Consider:</p>
<ul>
<li>/api/user/{id}/images -> router sends to handler which retrieves images by user id</li>
<li>/api/user/{id}/friends -> router sends to handler which retrieves friends by user id</li>
<li>/blog/{slug} -> router sends to handler which retrieves blog posts by blog post slug</li>
<li>/{unknown} -> router sends to handler (sub-router) which sends to another handler which processes the request.</li>
</ul>
<p>This ensures that known routes avoid the overhead required by unknown routes. However, I do suggest that if the routing must be dynamic to that degree, that there may be unresolved design issues in play (or maybe simply poorly named routes).</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传