The Julia Language Challenge
nextjournal.com
nextjournal.com
It doesn’t really matter if python doesnt have as powerful an array syntax as julia, if I can pull in numpy (a standard, battle-tested library) and produce equivalent (and good) code.
But if I pull in numpy, and still can’t produce code as nice as julia (because of whatever limitations of python as a language), then there’s an argument to be made for switching to julia.
But going purely off what the language gives by its initial installation... its just an arbitrary limitation to give the question one answer.
The article claims that it proves the point ... "This can be unfair since Julia has lots of functionality for array algorithms built in, but also illustrates the point" ... but assuming good intentions, I'm not sure what point that is? At best, you might be comparing APL languages to one another, but nothing broader than that.
But it's not clear to me that this "challenge" is intended to say that array operations are easy, for a newcomer to the language. As I understood it, it's an argument for Julia being better in the long run, for the code that requires "developer effort, performance and extensibility"; that is, elements of the language beyond the first few days of using it, for packages larger than what would be written by "amateur programmers"
If depicting usage for the amateur was the main point of the article, it totally went over my head
You understood that right, it is not :) That's why I wrote, that I'd probably need years to create the same implementation in any other language.
I do think it does have a widespread implication for the ecosystem though, and therefore at some point will also be relevant to newcomers.
In a language like Unicon/Icon, I can "$include" a source file, I can "link" an ucode (compiled source, unlinked) file, I can "import" a ucode class file, I can put all the source in the same file or I can get the compiler to bring all the files together on the command line and it will basically end up with the same result.
Does Julia provide Base as source to allow you to do anything like the above?
So the part that gets loaded when you load the language/compiler is what is referred to as Base. So, it's kind of impossible to not have it and it is part of the language. So it's somewhat like asking, what can you do without "importing" integers? (ok, that's a hyperbole, but you get the idea ;) ) But, the source code does get shipped, so you might actually go in there and change stuff if you want, and Julia will pick up those changes - even, to get back to that hyperbole, how integers are defined, since they're implemented in pure Julia. I'm not a 100% sure what you're aiming at, but does that answer your question? ;)
It does answer the question in that it appears that Julia, as a language, doesn't provide you with much to start with, not even the bare bones. I have seen commentaries back in the 1980's that discussed such language designs. It makes for some uniformity but does require much work to be done to create the basic infrastructure that would allow and enable others to use these kinds of languages out of the box. Interesting in some ways, I suppose.
Despite it has great libraries, I think it's the wrong language for scientific computing. In a nutshell, it's much harder than it should be to write concise and performant code. There's a huge stack of things used to achieve this, which often makes it complex to reason about performance and obscures the underlying mathematics: cpython, NumPy, pandas, Cython, Numba, PyTorch...
Julia is really appealing because it's very close to executable mathematics. And thanks to a few language design decisions, it's very easy to write performant code and reason about it.
For example, Julia ML libraries like Flux have full composability with Julia code. That's a game changer in terms of simplicity. Tensorflow is getting ported to Swift to mimic that feature.
> "Types & Functions From Different Packages It's mandatory to use an existing package without changing it. This can also be emulated by putting it in an isolated module not using any constructs defined in our implementation."
I don't know if the article has been revised, but this I read the rules about libraries completely opposite from you.
Setting up an artificial challenge with rules that favor 'your' language up front is a great way to get the result that you want, but not a great way to learn. Each language has its strengths and its weaknesses, but none of those strengths and weaknesses matter all that much from the point of view of an end user until they have a direct impact on the quality of that which is produced. And programmers have shown time and again they can write crappy code in just about any language (and good code too...) with the language features having a much smaller impact on the outcome than the quality of the people writing the code.
I'm familiar with Python scientific ecosystems and currently learning Julia and Rust. As far as I see it, these languages likely do not compete well with Julia on all three categories --- "developer effort, performance and extensibility" for the problem you picked, because none of them was designed for native array arithmetic with broadcasting in the first place (except Numba maybe).
The current dominance of Python in scientific computing is rather a beneficent coincidence, because most people were pretty fed up with C++ and legacy Fortran back then, and efforts to improve tools of the trade tended to converge on Python. Fast forward 18 years after Python was born, Julia came to light and it was designed to do numerical tasks fast and elegantly and to avoid some pitfalls of "scripting languages". Does the success of Julia in these tasks signify the incompetence of Python? I don't think so. It served well what it was designed for. And if you tell me Julia is the right track of the history, you'd have to wait for it to happen. (Don't get me wrong, I like the Julia language and use it for a side project. I just don't see bashing other languages to prove that Julia is the one language to rule them all would be fruitful.)
On the other hand, I'd say you forget to count in Fortran 2008! For broadcasting with multithreading, Fortran + OpenMP works pretty well.
It is a general purpose language, but I don't think it is that much better than python in the scripting space (probably less good for now).
https://en.wikipedia.org/wiki/List_of_cognitive_biases#Frequ...
The vast Julia PR machine is certainly earning its million dollar bonuses this month...
(But you're right, of course; with a stable release out, people are now able to use and promote the language more effectively.)
user_defined(x) + y + z
Which may mix matrices (x), vectors (y), scalars (z), and user_defined or built-in (+) functions applied elementwise; has automatic extension of dimensions; and must operate in a single pass, performing only one traversal.This problem statement is rather tailored to Julia’s way of doing things—I don’t believe any other language can really “succeed” as a result, and not because Julia is any better (nor worse).
In Haskell I might simply rely on laziness and rewrite rules for fusion, or reach for a package like repa¹ or folds², depending on the actual problem at hand. There wouldn’t need to be a library providing this “broadcast” functionality, because it can be expressed already with other composable tools.
If you work with the language and type system, there are lots of tools available for giving the compiler enough information about the structure of your code to guarantee good performance, abstracted behind a reasonable API. You might end up writing something more explicit, like:
zipWith3 (+) (userDefined <$> x) (extend y) (extend z)
-- With MonadComprehensions + ParallelListComp
[ userDefined a + b + c
| a <- x | b <- extend y | c <- extend z
]
But I’d be perfectly happy with that, since it uses abstractions like functors and monads that I already understand. I probably wouldn’t reach for the direct equivalent of this Julia solution, which would be to write some Template Haskell macro that rewrites an expression to insert dimensional conversions (“extend” in the pseudocode above) and fusion of operations—that approach feels too brittle and difficult to extend, and not because of any deficiency in the language.Actually, your sample "solution" is (was?) also a much used pattern in Julia - especially before we had such a powerful broadcast implementation.
So in Julia there is no need either, since it is already implemented much better in Base. I think you're overlooking the actual point of my challenge: it's not that we need a broadcast package, but that a lot of other scientific libraries will need to use similar patterns, to write new packages with new algorithms!
The point is also, that I can go into the Julia repository, and without a lot of time & effort, can improve the current implementation of our broadcast - because it is not that much code and pretty easy to follow.
So, an equivalent in your example would be, if you can quickly implement something like repa or folds in a similar minimal example like my Julia implementation.
If you don't like the details of my challenge, feel free to make a minimal folds implementation from scratch, and show me that the result is fast and composable ;)
To add some more historical facts to this: before broadcast, we mostly used maps & zips in Julia. Which is not very elegant if you want to write down your math. Even with broadcast, some math nuts still complain that it isn't close enough to the actual math :D So consider, that those people are Julia's user base ;)
And then, someone pretty much implemented broadcast in their freetime and added it as a PR to the Julia language. This is how the language grows, and a large part of that is possible, because it takes relatively little time to come up with a performant implementation of such a difficult problem.
And now we have broadcasting in the language, and maps + zips are indeed less idiomatic... Because anyone who started using the new broadcast immediately thought it was an improvement to "the old ways".
I'd be curious to see what kinds of implementations someone could come up with in CUDA using templates, though. It could easily blow the Julia performance out of the water.
And this is tuned towards package development, specifically generic package development. This is quite a hot topic in scientific computing. The challenge isn't for people who use packages, it's for people who develop packages and are looking to make them generally useful (i.e. not just single project).
Thus, a much fairer comparison, if we care so much about performance that non-SIMD execution is a non-starter, would be CUDA vs. Julia targeting the GPU.
Note that I absolutely love Julia because I find it far easier to write than a language like C++, but idiomatic Julia code still has low overhead, unlike, say, Python, which introduces orders of magnitude of overhead compared to low level languages, and you have to move away from simple idiomatic code to achieve reasonable performance. However, I find it unreasonable to focus on SIMD performance - the performance gain over non-vectorized code isn't that big, and if you do want to eek out that much extra performance, you should focus on using the GPU.
That's not true. There's a lot of heavily serial algorithms which do not always work well on the GPU unless they are of a very large size, larger than most applications. This happens a lot in optimization and differential equations, two of the pillars of scientific computing.
>Thus, a much fairer comparison, if we care so much about performance that non-SIMD execution is a non-starter, would be CUDA vs. Julia targeting the GPU.
Tim already wrote that article where it shows how Julia code generation can be faster in some instances for GPUs. https://devblogs.nvidia.com/gpu-computing-julia-programming-...
>but idiomatic Julia code still has low overhead
Where?
And those situations tend to get a very minimal speedup from SIMD.
>Tim already wrote that article where it shows how Julia code generation can be faster in some instances for GPUs. https://devblogs.nvidia.com/gpu-computing-julia-programming-....
Yes, that's great, and would have made for an interesting challenge, rather than focusing on SIMD instead of serial or GPU performance.
>Where?
I didn't word it well, but I meant to say that Julia doesn't have much overhead compared to C/C++, unlike Python (what overhead it does have comes from garbage collection).
Create a better string copy way:
while (*d++ = *s++);Also, heck, it's a challenge, we all want in, please make it so :)
If I can outperform Julia by 50% in C++, but I have to write 10x code. I won't bother with C++.
If you manage to have that in the end, that's a very valuable promotional information.
For one of my master courses, I had to write 200 lines in python. C++ solution would have been in the thousands. My colleague solved it in 10 R lines. R is on my list now :)
d := s
Strings are immutable, so it internally just copies a descriptor.
return memcpy (dest, src, strlen (src) + 1);
I think the logic is that strlen is virtually free for short strings, and that memcpy can be a lot faster for long ones (reading https://sourceware.org/git/?p=glibc.git;a=blob;f=string/memc..., it even copies “whole pages from SRCP to DSTP by virtual address manipulation, as much as possible”. strlen doesn’t do things the K&R way either anymore. It checks one byte at a time until things are word aligned, then checks words for zero bytes. That can read from past the string, so it requires the platform it runs on to make that OK. See https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strl...)Come on.
I do think, that it is quite showing, that google + tensorflow don't believe in the mix of python + c/c++. I've been working quite closely on the challenges that faces machine learning libraries at the moment, and I can totally see where they're coming from when they're unhappy with the current solution and want to rewrite it in swift. How should I express this in a better way?