Finally, note that by "at least an order of magnitude worse" I'm comparing it to hyper-optimised C++ that's designed for sub-millisecond latencies, as the C++ server used the same framework used in latency-critical HFT software.
Finally, note that by "at least an order of magnitude worse" I'm comparing it to hyper-optimised C++ that's designed for sub-millisecond latencies, as the C++ server used the same framework used in latency-critical HFT software.
This means that you can often control the pause time effectively if you are able to reduce the number of pointers in use. I have a server that has a large-ish working heap size (10-30GB) that consists of several very large maps; initially it gave pauses of 2-3 seconds. I got rid of pointer types from the map keys/values and the pause time became a few hundred milliseconds. At this point, the GC pause was mostly caused by internal overflow pointers from the hashmap implementation. I implemented my own pointer-free map type and brought the pauses down to < 1ms.
I also filed an issue (https://golang.org/issue/9477) and Dmitry Vyukov kindly implemented a change so that pointer-free maps are not scanned by the GC. This will be in Go 1.5 and I will delete my custom map.
type SpellCheckerMap struct {
Runes []rune
Nodes []*SpellCheckerNode
}
with only few runes, I can just go through the list to find the index of the following node. You save the computation of the map hash key. It is pretty fast, here is an example of a spelling suggestion over a 500,000 words corpus[1].Basically, the garbage collection cost forced me to think about a better data structure for my case. Maybe not that bad all in all.
[0]: http://stevehanov.ca/blog/index.php?id=114 [1]: https://www.chemeo.com/search?q=asparine
The are more compact representations, e.g. you can store the dictionary in a deterministic acyclic minimized finite state automaton (which can be stored in a flat array/slice). This gives you O(n) time lookups, where n is the length of the word, and reduces much of the redundancy in a dictionary.
Words within an edit distance can be found by computing a Levenshtein automaton for the word (which can be done in linear time) and computing the intersection language of the dictionary automaton and the Levenshtein automaton.
This approach is fast and very compact. I have a Java implementation:
For the moment I am looking at the Linkedin approach[0]. Sorry for keeping this semi-offtopic thread alive, but these problems are so interesting that I cannot stop.
I'd bet most of them, although they're almost all closed-source so we will likely never know.
message Foo {
required string a = 1;
required int32 b = 2;
optional int64 c = 3;
}
Generates a struct like: type Foo struct {
a *string
b *int32
c *int64
}
This is not particularly friendly to the garbage collector compared to a POD struct with no pointers.https://developers.google.com/protocol-buffers/docs/referenc...
The flow of data is currently:
various external sources -> merged into protocol buffer struct -> later sent to client
To avoid keeping a heap of protocol buffer structs in the heap, this would need to change to:
various external sources -> merged into some internal datastructure -> later converted into protocol buffer struct and sent to client.
Did you consider keeping strings (serialized messages) in your cache rather than message objects? I do this in C++ code simply for memory efficiency. Here it would also allow you to avoid these extra pointers.
Go does allow fixed size arrays in structs, and they're inline in the struct, so `struct { foo [8]uint }` is 32 bytes, whereas `struct { foo []uint }` is 12 bytes and `struct { foo string }` is 8.
You might want to spend some time optimizing allocations (http://blog.golang.org/profiling-go-programs for some info). I successfully eliminated these pauses in my project by putting data on the stack instead of the heap in critical points of my code. This can be done (though it's ugly) even when the size of particular allocations aren't known at compile time (i.e. use a fixed stack allocation with an appropriate upper limit, check for the limit and go to the heap only if necessary).
> I suspect that would complicate the code significantly
Maybe if your code has many hot spots with these allocations, but it's worth it to manually optimize these currently.
The code generation tools are better (what you call "compile time IO"), the IDEs are much better, it has generics which you seem to miss, performance is better (the GCs are state-of-the-art), monitoring is much better, the language is also regular and simple, and you don't have to write inheritance-heavy code if you don't like.
As someone who likes both Java and Go, I find it surprising that anyone would choose the latter for long-running server code, especially where performance matters. Go is great for quick command-line apps or very simple services, but when you need to build an important server, Java wins out every time.
Certainly for your particular requirements and preferences, Java seems to have all of Go's advantages and (almost) none of the disadvantages.
- Emacs - Java IDEs may look better, but this doesn't seem to be an issue for author. Also other tools are very mature for such youn language as Go.
- Goroutines - Java has no built-in equivalent or one opinionated way of green-threading, only frameworks requiring months of expertise. From perspective of person, who need to switch language quickly this is very discouraging.
- No inheritance - Java design patterns are clearly no-no in the described context (they were presented as anti-pattern).
- Built-in, effective templating - again one easy path to start with, also powerfull enough to write your own tools if needed.
Go is simply easier/faster to start with and most of the performance problems were not GC-bound, so JVMs maturity wasn't such an advantage.
https://github.com/puniverse/quasar (I'm the main author)
> Built-in, effective templating
So does Java (and for a long time): http://docs.oracle.com/javase/7/docs/api/javax/annotation/pr...
> and most of the performance problems were not GC-bound
The author's complaints were about GC pauses, and besides, Java is faster even for non-GC bound tasks.
> Go is simply easier/faster to start with
Maybe, but just a little, and mostly because Java has (too?) many libraries to choose from.
Even if the differences you highlight are indeed true (a point on which I disagree), those differences are, at best, quite small, while the advantages in Java's favor are much bigger, certainly in those areas that matter to the author.
As pjmlp said, the issue of "array of structs" is being addressed in Java 10. In the meantime, for contiguous memory allocation, you can make use of off-heap memory (which also helps those "more complex forms"). But the flip side is that Java's memory allocation is a lot faster than C's (i.e. in throughput -- not latency, as there are GC pauses), and most GCs are copying collectors that automatically arrange objects contiguously (though it's far from being good enough for arrays of object, as you need to follow references, and every object carries additional overhead, which is precisely why this is being addressed in Java 10).
However all JVMs have state of the art profilers like e.g. Visual VM, Java Flight Recorder and many others that help track down which data structures might need some help.
Most of the time you don't need to, because GCs are that good, and escape analysis can avoid heap allocation automatically[1]. You won't get C++ latency, but you'll do better than Go. If you really need to avoid allocations for some reason, you can go off-heap for C++ performance (Java HFT applications do that for the most performance-critical things; it requires more work, but overall, less than C++).
Also, I'm not saying that Java is always the better choice (the JVM needs some time to warm up; Go's runtime is statically compiled into the native binary artifact), but in this particular case it seems to be exactly what the author was asking for, that it's really the obvious choice.
[1]: http://psy-lob-saw.blogspot.com/2014/12/the-escape-of-arrayl...
Even though Go's runtime is statically linked while Java's isn't, Java is very much a C++ replacement in many circumstances, while Go makes for a terrific Python replacement if you need fast scripts and command line tools (which is why you mostly see Python->Go and C++->Java transitions). Not that Go is always more appropriate than Python or Java is always better suited than C++, but at least those are the common alternatives, and the ones that make the most sense considering the design decisions of those languages.
For you particular needs, I have no doubt you'll find Java to be the more appropriate choice.
[1]: Like this: http://openhft.net/
Hype.
N.B. we use Go for real-time bidding (in the context of programmatic buying of ad space), and can easily respond to 6000 QPS on a single server within a 100ms time frame (from the SSP's POV), with a working set of about 2-3 GB that we constantly keep in memory.