HN user

leiroigh

185 karma
Posts0
Comments64
View on HN
No posts found.

I'll be interested in seeing the fallout of the (unavoidable) compat issue:

If I have a function that has a value `x` that erases to `java.lang.Object` (e.g. a parametric function with no lower bound); then it used to be safe to check for nullity and then synchronize on the object.

This is no longer safe: This can now throw `IdentityException` into your face. (it was _never_ a good idea)

In other words, a lot of old code must be reviewed.

I suspect that `-XX:DiagnoseSyncOnValueBasedClasses=2` will need to stay (with the semantics: if user tries to synchronize on identity-less object, then log a JFR event and make it a NOP, don't throw an exception)!

The current JEP text is a little too ambiguous to figure out whether that is the plan, anyways.

Lenses in Julia 9 months ago

Yes. O(1) snapshots are awesome! Persistent datastructures are a monumental achievement.

But that comes at a performance price, and in the end, you only really need persistent datastructures for niche applications.

Good examples are: ZFS mostly solves write amplification on SSD (it almost never overwrites memory); and snapshots are a useful feature for the end user. (but mostly your datastructures live in SRAM/DRAM which permit fast overwriting, not flash -- so that's a niche application)

Another good example is how julia uses a HAMT / persistent hash-map to implement scoped values. Scoped values are inheritable threadlocals (tasklocal; in julia parlance, virtual/green thread == task), and you need to take a snapshot on forking.

Somebody please implement that for inheritable threadlocals in java! (such that you can pass an O(1) snapshot instead of copying the hashmap on thread creation)

But that is also a niche application. It makes zero sense to use these awesome fancy persistent datastructures as default everywhere (looking at you, scala!).

Lenses in Julia 9 months ago

There is nothing counter-intuitive or julia-specific about it:

Fastest way is to have your datastructure in a (virtual) register, and that works better with immutable structures (ie memory2ssa has limitations). Second fastest way is to have your datastructure allocated on the heap and mutate it. Slowest way is to have your datastructure allocated on the heap, have it immutable, copy it all the time, and then let the old copies get garbage collected. The last slowest way is exactly what many "functional" languages end up doing. (exception: Read-copy-update is often a very good strategy in multi-threading, and is relatively painless thanks to the GC)

The original post was about local variables -- and const declarations for local variables are mostly syntactic sugar, the compiler puts it into SSA form anyway (exception: const in C if you take the address of the variable and let that pointer escape).

So this is mostly the same as in every language: You need to learn what patterns allow the current compiler version to put your stuff into registers, and then use these patterns. I.e. you need to read a lot of assembly / llvm-IR until you get a feeling for it, and refresh your feelings with every compiler update. Most intuitions are similar to Rust/clang C/C++ (it's llvm, duh!), so you should be right at home if you regularly read compiler output.

Julia has excellent tooling to read the generated assembly/IR; much more convenient than java (bytecode is irrelevant, you need to read assembly or learn to read graal/C2 IR; and that is extremely inconvenient).

The bounce is invisible from the outside -- an event horizon means causal decoupling. From outside, the formation of the black hole looks like the good old "frozen star" picture.

There will never be observational evidence on what happens on the other side of any event horizon, you'd have to cross over to the other side to see it for yourself (but you won't be able to report back your findings). There's a fun greg egan short story about that ;)

I have not really looked at the summary, opted to go straight to the source.

This identification happens in equations 31-34 on page 7f subsection "Cosmic Acceleration" in https://arxiv.org/abs/2505.23877

The justification looks super sketchy and hand-wavy to me, though, which I summarized as "somehow (?)".

"After inflation the event horizon would not exist."

Apparent cosmological constant viewed from the bouncing inside induces a cosmological horizon, which they identify with the black hole horizon viewed from the outside. Super elegant idea, but I don't buy that this is supposed to be true.

in a higher-dimensional parent universe

That's incorrect: The parent universe is not higher-dimensional, it's the same good old 3+1 as our universe.

What they propose is: Let's take our good old GR, and start with a (large, dilute) compactly supported spherically collapsing collapsing cloud of matter. During that, you get an event horizon; afterwards, this looks like a normal black hole outside, and you never see the internal evolution again ("frozen star", it's an event horizon). Inside, you have the matter cloud, then a large shell of vacuum, then the event horizon.

Quantum mechanics suggests that degeneracy pressure gives you an equation of state that looks like "dilute = dust" first, and at some point "oh no, incompressible".

They figure out that under various assumptions (and I think approximations), they get a solution where the inside bounces due to the degeneracy pressure. Viewed from inside, they identify that there should be an apparent cosmological constant, with the cosmological horizon somehow (?) corresponding to the BH horizon as viewed from the outside.

All along the article, they plug in various rough numbers, and they claim that our observed universe (with its scale, mass, age, apparent cosmological constant, etc) is compatible with this mechanism, even hand-waving at pertubations and CMB an-isotropies.

This would be super cool if it worked!

But I'm not convinced that the model truly works (internally) yet, too much hand-waving. And the matching to our real observed universe is also not yet convincing (to me). That being said, I'm out of the cosmology game for some years, and I'm a mathematician, not a physicist, so take my view with a generous helping of salt.

(I'm commenting from "reading" the arxiv preprint, but from not following all computations and references)

PS. I think that they also don't comment on stability near the bounce. But I think that regime is known to have BKL-style anisotropic instability. Now it may be that with the right parameters, the bounce occurs before these can rear their heads, and it might even be that I missed that they or one of their references argue that this is the case if you plug in numbers matched to our observed universe.

But the model would still be amazing if it all worked out, even if it was unstable.

so unfortunately

I see a fellow enjoyer of bugs ;)

vmap afaict only exposes push-back and pop-front for mutation

what about https://doc.rust-lang.org/nightly/std/io/trait.Write.html#ty... ?

and critical methods aren't inlined

aren't inlined explicitly. This does not mean that they are not inlined in practice (depending on build options). Also, LLVM can look inside a noinline available method body for alias analysis :(

This is a big pain whenever one wants to do formally-UB shennenigans. I'm not a rustacean, but in julia a @noinline directive will simply tell LLVM not to inline, but won't hide the method body from LLVM's alias analysis. For that, one needs to do something similar to dynamic linking, with the implied performance impact (the equivalent of non-LTO static linking doesn't exist in julia).

The main problem with that is that it doesn't play nice with most languages. Consider

  int foo(int* ptr) {
    int x = ptr[1<<16];
    *ptr += 1;
    return x + ptr[1<<16];
  }

Compilers/languages/specs tend to decide that `ptr` and `ptr + (1<<16)` cannot alias, and this can be compiled into e.g.
  foo(int*):
        mov     eax, dword ptr [rdi + 262144]
        inc     dword ptr [rdi]
        add     eax, eax
        ret
which gives undesired results if `ptr` and `ptr + (1<<16)` happen to be mapped to the same physical address. This is also pretty shit to debug/test -- some day, somebody will enable LTO for an easy performance win on release builds, and bad code with a security vuln gets shipped.

That's pretty cool.

Normally it would be the either the programmer's or the compiler's job to unroll a loop and then reduce dependency chain lengths.

But its nice if the renamer can do that as well.

Presumably intel have real-world data that suggest that significant real workloads can profit from this.

I wonder whether that points to specific software issues, like hypothetically "oh yeah, openjdk8 hotspot was a little too timid at loop unrolling. It won't get that JIT improvement backported, but our customers will use java8 forever. Better fix that in silicon".

^this!

In garbage-collected languages, please give me gradual / optional annotations that permit deterministic fast freeing of temps, in code that opts in.

Basically to relieve GC pressure, at some modest cost of programmer productivity.

This unfortunately makes no sense for small bump-allocated objects in languages with relocating GC, say typical java objects. But it would make a lot of sense even in the JVM for safe eager deterministic release of my 50mb giant buffers.

Another gradual lifetime example is https://cuda.juliagpu.org/stable/usage/memory/ -- GPU allocations are managed and garbage collected, but you can optionally `unsafe_free!` the most important ones, in order to reduce GC pressure (at significant safety cost, though!).

This is awesome and the first precedent I've ever seen for a standard library doing the right thing on rand floats. Big kudos to the zig people and thanks for brightening my day!

Fair enough, if a user asks for a random float between [0, 1e-38f] then subnormals are expected.

I was just thinking about the (0,1) case, under the mistaken assumption that one could map it to (a,b) via multiplication/addition, but you're right -- if you want (a,b) perfectly () then it's not obvious to me.

() up to inaccuracies of cosmologically negligible scale

Comparability between implementations: Say GPU vs CPU, or between languages, or to pseudocode in old papers.

Typical example where the badness of the floats bites you is if you do something like log(rand()), or 1/x, or more generally you map your uniform (0,1] interval via a cdf to generate a different distribution, and your mapping has a singularity at zero (this is like super standard -- e.g. how you generate exponentially distributed numbers. Or if you generate random vectors in an N-d ball, you're using a singular cdf to compute the magnitude your vector, multiplied with a normalized normal distributed N-d variable to generate the angular component).

Then the bad random floats (i.e. the non-smooth distribution close to zero) introduce real and visible bias after transformation. Afaiu the problem is well-known and serious people correct for it somewhere. If you fix the underlying problem without revisiting the now-obsolete fixes, then your results are biased again.

I'm not arguing for keeping the bad random float generation everywhere. I think it should be fixed, not just in julia but everywhere. I'm just saying that it's not a no-brainer and there is discussion and compat and communication and code audit involved in doing such a momentuous change.

Also, I'm not really up to putting in the work to champion that at the moment (arguing on the internet doesn't count ;)).

You think there might be applications like complex very large simulations where an event with probability 1:10^38 matters?

You are aware that the current age of the universe is < 10^27 nanoseconds, just for comparison?

I don't think that would be a good idea -- sticking to positive normals and truncating below is enough for float32 and float64. I mean, consider that 1.1754944f-38 is a normal 32 bit float.

The probability that your "perfect" code would ever get triggered is such that humankind has not built enough compute to expect to get that unlucky yet.

(you may ask: why then care about that possibility if it's dead code anyway? Because of security vulns in context of flawed RNGs. Bad underlying RNG leading to bad distributions is expected; bad underlying RNG leading to RCE is not OK. So don't ever output zero or subnormals!)

For bfloat16 the same holds; for ieee float16, well, I have no clue what we want.

That thread was in 0.6 days on my long-dead broadwell using DSFMT as a C library with afaiu hand-written intrinsic code for bulk generation of floats. We switched RNG to xoshiro in the meantime which is faster and generates 64 bit numbers natively (as opposed to dsfmt which generated 53 bit natively...). So don't trust that these old timings represent current julia; I updated the thread with current timings.

I'd be very happy if this can of worms could be reopened, but am currently not active enough in julia dev to champion it.

Also somebody would need figure out something very clever for AVX2 / NEON. (AVX512 has the required instructions)

Also I can't imagine the mess with GPU -- if rand statistics differ widely between CPU and GPU that's a no-go, and I don't know what works well on which GPUs.

This problem is much more acute in Float32.

Another sample implementation (maybe easier to read?) is in https://discourse.julialang.org/t/output-distribution-of-ran...

As far as I remember, the main reasons for not rocking the boat on that in julia were: Everybody generates rand floats wrong, so doing it right breaks expectations and compat; and there is a perf price of maybe 0.5-1.5 cycles/number to pay; and this is nontrivial to SIMD (basically because we don't have simd TZCNT / LZCNT / access to exponential distribution)

Nobody complains about julia for "shelling out". The imo justified complaint is that julia sucks at "shelling in".

(OK, the pipe buffering rules still annoy me)

Similar with FFI: Julia has awesome support for calling out to other languages; support for getting called by other languages is suboptimal.

It's not.

But people who like the language will invariably use beyond its core competency.

Hence it is important to ensure that julia is "kinda mediocre" for CLI scripting (big step up from "absolutely terrible"). Personally, my familiarity with julia and its features outweighs the fact that python/perl/bash would be the better tool for many CLI scripts.

Speed depends on workload.

Fast startup, high throughput, high productivity: Choose two.

C/C++/Fortran take fast startup and high throughput, python takes fast startup and high productivity, and julia takes high productivity and high throughput.

Some have been partially addressed.

But the main point is that the critique is misguided in its generality. If the author cares about running many small scripts that each take a handful of milliseconds, then julia is just not the right tool for his job. No need to write overly general angry posts like "julia is slow"; instead write "julia has sluggish startup time". This is to some extent unavoidable, since julia has a quite heavy runtime (need to load llvm). For some workloads, bash / python / perl are more appropriate tools.

To give you an example, `$ time julia -e "print(5)"` gives me about 230 ms, compared to python 35 ms.

The language is designed for longer running programs that compute heavy stuff. And it performs very well at its intended use.

I hate how the article waffles between "almost none", "virtually none" and "negligible".

There is a word with precise mathematical meaning that matches naive non-mathematical intuition: "almost all" / "almost none" (aka full measure set).

This concept is easy to explain to lay people: count up lengths of intervals, do some limiting process trickery to deal with the fact that the set cannot be written as a finite union of intervals. Alternatively, choose a real number uniformly at random from some interval, probability one will hit the "almost all" set and probability zero will hit one of the others.

"negligible set" is often used to describe sets that have zero measure and small Baire category. "Virtually" also has different mathematical connotations.

Upside is that it was well explained that the contents of the new result is "don't worry about overlaps between different denominators".

Big difference is that Cilk appears to typically synchronize on function return, while julia needs an explicit wait/fetch (there is also a @sync macro). That is, spawn/wait can be matched from the AST in cilk, while julia demands that the user communicates and stores a reference to the started task for later waiting.

It is easy to forget to wait on a task, or even lose reference to it. The current scheduler will typically execute this task quickly, even if nobody ever waits on it; until load changes and you have races everywhere. There are currently no warnings comparable to Python's "RuntimeWarning: coroutine foo was never awaited".