> The standard JVM garbage collection strategy is “stop the world.”
The default collector in HotSpot does, I think, stop the world when collecting. But it also does multiple small collections between larger major collections. It, by default, optimizes for throughput over latency since most applications care more about overall throughput than low latency.
Even with the default, you can tune the maximum latency if you want to sacrifice throughput:
Not only that, but HotSpot also features a true concurrent collector, which is a command-line flag away.
I specifically came here to write something very similar. Thank you. Call me a JVM fanboy, but when I see such comments, I tend to believe the author didn't want to spend time genuinely understanding the JVM and doing thorough testing.
I don't want to discard per-actor heap memory in Pony. That's pretty slick. Pony has been on my radar for a while because it does a lot of things well.
It's hard to tune a GC to be super-fast. It's easy to profile and find your biggest memory leaks.
Now they are trying to fit them in without breaking backwards compatibility.
Simplicity can be sacrificed for performance. It is more important for the interface to be simple than the implementation. The faster the programmer can get stuff done, the better. It’s ok to make things a bit harder on the programmer to improve performance, but it’s more important to make things easier on the programmer than it is to make things easier on the language/runtime.
This is an excellent design decision. Yes, performance sometimes introduces complexity, and I have been in teams where the philosophy is: "It does not matter if it is slower, but any high schooler that knows HTML has to be able to understand it". The purpose of a program is to satisfy the users, not the developers or the managers, and if the next developer needs to study a thing or two before understanding the code that is OK.
Simplicity should be sacrificed for performance.
This is self contradictory. Programmer resources need to be allocated to pleasing the users. To please users, you need a certain amount of performance. Performance is not simply and end in itself. Programmer resources can be used to please users in other ways.
Simplicity should be sacrificed for performance.
For example, code that effectively makes the same decision three times is both slow and obscures the intent of the code.
And there are ways to compartmentalize optimizations so that people working in the general vicinity don’t have to bother with the ‘clever’ code on a daily basis.
The idea is if I have to choose between simplicity and performance, I shall choose performance.
Your example does not make sense because you are not choosing simplicity over performance because the code was already performing slow.
Compartmentalizing the code to hide the complexity behind a black box does not apply either because you are not making a complex code simpler, the one that has to maintain your black box is still exposed to the "clever" code.
A block that scans a table for a match can be moved up and out and then replaced at your leisure with a version that is more sophisticated and faster and the people using it only have to look at it if there’s a bug. They don’t have to look at it then they don’t have to grumble about how it took 10 lines of comments to document four lines of code because you used some uncommonly known property of Logic or Set Theory to eliminate a bunch of duplicate work.
And worst case they can revert the changes until they sort it out because it’s just one function with the same interface. (Also, they won’t fight you as much about the initial change because it’s easy to revert and self contained)
But it seems the documentation is a bit out of date - I cannot even get the "Hello, world"-program to build correctly, neither on Debian Stretch nor on openSUSE Tumbleweed.
It is quite frustrating if you try to learn a new language, and when you make your first step, you step on a nail.
And it is even more frustrating, because Pony looks very interesting, for the reasons the linked article explains. ):
UPDATE: On Debian, telling the pony compiler to --pic does the trick.
UPDATE2: On suse, installing binutils-gold solved the problem.
UPDATE3: Now we're talking! This language is very, very interesting, indeed!
If you like you can hit up the mailing list (https://groups.io/g/wallaroo) or the IRC channel (#ponylang on freednode, https://webchat.oftc.net/?channels=wallaroo) to see if anybody can help, or if you think you've found a bug you can file an issue in github (https://github.com/ponylang/ponyc/issues).
UPDATE: I'm glad to hear you got it working!
Me, too! This looks pretty exciting!
Happy to answer any questions here or if you prefer, via email:
I'm guessing that tooling for Pony still needs some refinement, but as things are now what would you recommend to someone new to Pony (e.g. editor, package manager, build tools, debugging tools)?
We did consider Rust as there are certainly overlaps in their approaches to safety (though coming from different angles). In the end we thought that Pony would be better for managing concurrency for our particular use cases, which we thought mapped well onto the actor model.
Out of curiosity, were there a widely accepted actor lib/approach in Rust (I know there are a few like RobotS and others), would that have affected your decision?
That said, I mean, if you want to go full actors, Pony is a great choice.
I've been using Emacs as an editor. We use a combination of make and pony-stable to build projects. For package management we use pony-stable. LLDB is your best bet for debugging, and if you follow the link above there's a link to the pony-lldb project, which is an LLDB extension that makes it easier to work with a few Pony datatypes.
At the time we made the decision, we were worried about Erlang being able to meet the latency and throughput goals we had. I knew a number of people who worked on Riak at Basho and had a few lengthy discussions about Erlang performance. They felt that we could end up struggling to get the performance we were looking for.
I had never used Erlang for any large scale project and deferred to their wisdom and knowledge.
As it turns out, the approach that we've used to support languages like Python is in process, using C to bind Wallaroo with the other language. This probably would have been more difficult with Erlang as well, but that's hindsight.
Erlang is an awesome language. It has an amazing VM. That we didn't think it was right for us should in no way discourage anyone else from using it.
Dropping Python into the mix for a high throughput processing pipeline seems counterproductive. Why isn't there a tutorial in a more strict language like Go or C++, since someone pursuing this kind of high-throughput framework that can't benefit from wider parallelism, will 100% want that performance guarantee?
Kafka and framed TCP are the two ingestion sources that Wallaroo currently ships with.
We added a Python API because there was a lot of interest in it. There's more to Wallaroo than just performance and folks were interested in having it available via Python. See https://vimeo.com/234753585 for some more information on the "scale independent" nature of the Wallaroo APIs.
We're working on that right now actually. Planning to release later this year.
We had a C++ API (still do), we aren't currently supporting it. There was limited interest at the time. If folks show interest we would start supporting it again.
After reading the Pony guiding principles, I was delighted. This looks like an implementation where the ecosystem complexities don’t get in my way of „getting things done“!
I like my languages opinionated, in as "there should be one way to do it". Python, C, Go, Clojure, maybe Rust. Designed to be a tool for their inventors set of problems, not a vehicle to implement research papers. Fast turnaround/feedback cycles.
Hmm, I actually see that as a strength. Need a strong type system, pure FP, and immutable state throughout? Easy. Need to build an OOP library that will be mostly used by Java devs? Easy as well.
Implicits are handled pretty well by Intellij IDEA afaik.
Of course, I haven't had experience using Scala in a large codebase, so I may be on the wrong track!
I have a question on this quote
> The standard JVM garbage collection strategy is “stop the world.” That is, when the JVM needs to reclaim unused memory, it needs to pause all other processing so it can safely garbage collect. These pauses are sometimes measured in seconds. That is going to destroy your tail latencies.
Thats why applications rarely use the default serial collector.
Does your comparison still hold against the CMS and G1 collectors, which do a much a better job at eliminating "stop the world pauses"?
At my previous job, we used the G1 collector and still had issues with "stop the world" type pauses. G1 does a best effort, but I've had applications that still experience rather long pauses (sometimes measured in seconds, usually hundreds of microseconds).
I haven't used either CMS or G1 heavily since I joined Wallaroo Labs a couple years back so, I don't have first hand experience with either recently.
Azul's Zing JVM does a really nice job of concurrent collection and if you can afford it, is a great way to improve the performance of clustered JVM applications.
Biggest strength would be performance. Biggest weakness would be maturity. I think almost every pro/con I can think of can fall into those 2 buckets right now.
I'm a big fan of type systems so Pony having one is a big win for me.
The maleability of Pony and its immaturity helped us in some ways. We were able to treat it as a runtime for us to help mold and fit to our needs. That wouldn't have been possible with Erlang.
Has there been any progress towards a preemptive scheduler? Otherwise, I'd think that's the most obvious weakness.
I've heard rumor of someone working on a preemptive scheduler. The current cooperative one is about to have runtime backpressure added to it which is going to be a really nice scheduling win.
I've worked in Erlang-land far longer than I've lived in any OOP-land, so I'm not sure what you mean by enterprise'y OOP. Coincidentally, Pony's rules for "Packages" are something I just smacked my ignorant head against a few hours ago. The subsections of https://tutorial.ponylang.org/packages/ in the tutorial can probably answer at least some of your question: specifically "Package System" and "Use Statement".
So, does it?
Fields and methods can be public or private. Private is akin to Java's package private.
There are no instance variables.
Pony classes are defined using a "class" keyword. Classes have properties and functions, where functions are like methods that you would find in a language like Java. Pony supports structural subtyping via interfaces and nominal subtyping via traits, and it disallows multiple inheritance.
I'm not sure how much that helps, but if you have more specific questions I'd be happy to try to answer them. I'm a little rusty on my Erlang, but hopefully it will come back to me.
It was surprisingly easy to learn - I found the capabilities system rather intuitive compared to other methods for safely managing data races. Also the syntax is really simple and easy to read!
"ZGC has been designed with the following goals in mind:
- Handle multi-terabyte heaps
- GC pause times not exceeding 10ms
- No more than 15% application throughput reduction compared to using G1"
On the flip side, I can't help but wonder how likely they would have been to use Pony had none of their core team members been active in the Pony community.
We weren't active in the Pony community until we decided to use it for Wallaroo. We felt it was very important to invest in the improvement of Pony. And to do that, we needed to be part of the community.
Pony became our runtime instead of writing one ourself. That doesn't mean we don't have to work on our runtime. It means we are sharing the burden with others.
Highly concurrent: check.
Predictable latencies: I think that's a check, the go gc has had alot of engineering put into it to provide good pause times. There are some edge cases still.
Data safety: I think go is weakest here in terms of language design. Go does have go build -race which works pretty well for me, but I can see how some wouldn't consider it sufficient. It also wouldn't catch single threaded ownership problems, which can happen.
Easy way to interact with other languages: via CGO and buildmodes you can trivially call go from other languages. Some don't like that the runtime is still shipped and started, but the fact remains that you can cffi go functions from python very easily.
I'll have to check out pony this weekend. I've read up on it a bit but haven't compiled anything.
I'm an engineer at Wallaroo Labs and I've been learning Go so that I can add a Go API to Wallaroo. Your analysis looks pretty spot-on based on my experience.
The buildmodes is go definitely make it easy to call Go from other languages. The trickiest thing I've run into so far with calling Go is that you aren't supposed to hold on to Go pointers outside of Go code, so I'm having to jump through some hoops to hold on to objects between calls.
I have an RFC for Pony that I need to finish up that will let you call Pony function from C. I'm planning on getting around to that soon, as I think it will help improve our FFI story.
If you ever want to talk Pony, you can find me lurking in the Pony IRC.
Pony uses much less memory, there's no beam overhead, the GC protocol is better, the object overhead is much smaller.
Pony allows zero-copying messages (call-by-ref vs call-by-value), which allows using fast shared memory threading models (besides copying values). All this is compile-time safe via the type-system.
The pony stdlib does not support blocking IO, which is probably a good decision, but you need more overhead adding the wait logic by yourself.
Erlang supports distributed actors, pony not yet. Sylvan and Sebastian are working on it, but it's not easy. See https://www.ponylang.org/media/papers/a_string_of_ponies.pdf
Erlang supports macros via elixir, pony not.
Pony's capability-bases type system is far too advanced for a regular user. You need much longer to learn it and come up with compilable designs. This might be frustrating.
There is currently no equivalent to Distributed Erlang, but that's something that the creator of the language has been looking into.
At this time, there is no "in the wild" implementation.
When using async based APIs with something like Akka, passing around futures and such, and leveraging multiple actors in thread pools, doesn't that get you to approximately the same place as Erlang?
Sure, the JVM isn't doing the preempting, but the OS is. Is the main downside per-thread resource consumption and context scheduling overhead?
I do scala/akka in my dayjob and the amount of time we spend tweaking threadpools (execution contexts) and retesting just to get okay performance is insane. You wear a pretty big cost when you layer on abstractions like that.
I'm curious why you didn't choose Kotlin with Vert.X . Kotlin is a small town language that won only because it was loved by the community...Nobody "pushed" it. And it has won - it's now officially supported by Google.
Vertx is a superb actor model framework with first class kotlin support. Its extremely high performance (https://www.techempower.com/benchmarks/#section=data-r8&hw=i...) and is pretty popular (http://vertx.io/whos_using/)
From a forward looking perspective, why Pony ... Especially if it's development has stalled.
Pony is being actively developed, and has been as long as we've been using it. Since some of our team are now core contributors to the language, we've also invested in improving Pony, which has proven to be very useful to us.