<p>I'm working on a simulator where every instruction counts. We were drawn to go because of the interface abstraction, but from benchmarking, it is 5X worse to invoke the indirection of an interface rather than to call a method directly. However, our project structure now relies on the use of interfaces to get around the circular dependencies. Is there a way to either make interfaces faster (doubtful) or get around circular dependencies without interfaces? </p>
<hr/>**评论:**<br/><br/>Fwippy: <pre><p>You can restructure your packages so that you don't need to circular-import.</p></pre>goibm: <pre><p>Yeah that is a good idea.</p></pre>kjk: <pre><p>A simple solution to circular dependencies: put everything in one package.</p>
<p>Then you can gradually refactor your project to extract reusable parts into their own packages.</p>
<p>I don't know your code but it's not uncommon for people to go overboard with packages which then causes self-inflicted problems like circular dependencies between packages.</p>
<p>Or to put it differently: the existence of circular dependencies between packages is often a sign that those packages are not independent and should be merged into one.</p></pre>kl0nos: <pre><p>Interface is not a cost free abstraction. If you don't want to write more code then you use interface and pay the price in performance for productivity boost. You don't want performance penalty then you write more code/rewrite dependencies. You can't have it all, if you want performance and if REALLY every instruction counts (which i doubt if we are talking about CPU instructions, because you picked Go) then pick C/C++/Rust that have almost cost-free abstractions. In either way you pay with productivity for performance.</p></pre>egonelbre: <pre><p>If every instruction really counts, then write it in assembly. :)</p>
<p>It's hard to give any good suggestions without seeing the actual project structure.</p>
<p>Separate the important pieces into the tightest loop possible <a href="http://play.golang.org/p/DuCOPgbSgx" rel="nofollow">http://play.golang.org/p/DuCOPgbSgx</a>, use interfaces to hide that Run, and supply some arguments "when to stop". I.e. don't use fine-grained interfaces.</p>
<blockquote>
<p>but from benchmarking, it is 5X worse to invoke the indirection of an interface rather than to call a method directly.</p>
</blockquote>
<p>This doesn't look right, how are you calling things exactly? Are you using <code>interface{}</code> by any chance? <a href="http://play.golang.org/p/9POLvsjDF6" rel="nofollow">http://play.golang.org/p/9POLvsjDF6</a>, i.e. the slow part is converting to a particular interface.</p>
<pre><code>BenchmarkDirect-8 300000000 4.26 ns/op
BenchmarkInterface-8 300000000 5.24 ns/op
BenchmarkInterfaceUnknown-8 50000000 35.4 ns/op
</code></pre></pre>goibm: <pre><p>Yes you are right that if we truly wanted pure performance we would go with assembly, but since that is what we are simulating that amounts to translating it by hand. We wanted a language that would have speed enough while being easy to write and maintain. We were trying to see how much we could get out of Go with a core simulation.</p>
<p>Here are the tests used to determine the performance.</p>
<p><a href="http://play.golang.org/p/o6RagbXVTT" rel="nofollow">http://play.golang.org/p/o6RagbXVTT</a> -- This is the reference
<a href="http://play.golang.org/p/MdP6XgBch_" rel="nofollow">http://play.golang.org/p/MdP6XgBch_</a> -- This is the Test
I got:</p>
<pre><code>BenchmarkGetXInderectStruct-8 2000000000 1.50 ns/op
BenchmarkGetXManually-8 100000000 15.1 ns/op
BenchmarkGetXerectStruct-8 2000000000 1.65 ns/op
BenchmarkGetXInderectInterface-8 100000000 22.7 ns/op
BenchmarkGetXerectInterface-8 100000000 13.5 ns/op
BenchmarkSetXInterface-8 100000000 14.5 ns/op
BenchmarkSetXStruct-8 1000000000 2.71 ns/op
BenchmarkSetXManually-8 1000000000 2.76 ns/op
BenchmarkSetXReflection-8 50000000 26.9 ns/op
</code></pre></pre>egonelbre: <pre><p>I don't think these are very realistic benchmarks. It seems that the compiler is getting rid of some of the instructions completely.</p>
<p>Try without inlining, <code>go test -gcflags="-l" -bench . .</code>:</p>
<pre><code>BenchmarkGetXInderectStruct-8 100000000 15.3 ns/op
BenchmarkGetXManually-8 100000000 20.6 ns/op
BenchmarkGetXerectStruct-8 100000000 15.2 ns/op
BenchmarkGetXInderectInterface-8 30000000 48.3 ns/op
BenchmarkGetXerectInterface-8 100000000 16.5 ns/op
BenchmarkSetXInterface-8 100000000 20.4 ns/op
BenchmarkSetXStruct-8 100000000 18.4 ns/op
BenchmarkSetXManually-8 500000000 3.25 ns/op
BenchmarkSetXReflection-8 30000000 51.0 ns/op
</code></pre>
<p>I'm not sure whether in your real-world situation the inlining can be done.</p>
<p>Either way, if you want to get rid of the overhead, don't do the method calls (either on struct or interface).</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
0 回复
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传