HN user

Doradus

44 karma
Posts0
Comments27
View on HN
No posts found.

Your history is a little mixed up there. The original author of Turbo Pascal, and Chief Architect of Delphi, became the Lead Architect of C#. It’s not like Microsoft came out of nowhere and ate Borland’s lunch.

I expect discriminating by amount of relevant experience is fair, but that's not the same as age.

As someone who started coding at age 9, I agree. :-)

It's not about hiring someone in their 40s per se, but rather about recognizing the value of 20 years' experience. Age alone doesn't impart wisdom.

Agreed. I remember being in my early 20s, watching my more experienced colleagues, and realizing this. I had had a different (incorrect) mental model: I had thought intellect was as good as experience, so Ability = Intellect + Experience. I was wrong.

It seems absurd that an article claims to disprove a hypothesis linking structure to communication patterns without even the slightest mention of trying to observe those patterns. But they go even further: they claim to have determined the direction of causality between two variables without even having measured one of them!

React v15.5.0 9 years ago

Huh? That's funny--I would have said the opposite. It was from watching Obama's speeches that I learned that silent pauses can be much less annoying than "umms". If you want to see someone using a lot of "umms", watch Justin Trudeau.

Java Without If 9 years ago

It doesn't take long to get used to that style. It took me maybe three weeks of playing with Java 8 streams in my spare time before I got quite comfortable with it.

RAII is using one C++ misfeature (destructors) to work around another misfeature (inability to do cleanup when leaving a lexical scope). Go has a far more elegant solution to the latter; if you're not familiar with the defer statement, check it out.

Newcomb's paradox 11 years ago

Did you read the article? It answers your question. Take a look at the section that begins with this:

The problem is called a paradox because two analyses that both sound intuitively logical give conflicting answers to the question of what choice maximizes the player's payout. The first analysis argues that, regardless of what prediction the Predictor has made, taking both boxes yields more money.

If you don't find this convincing, that's the point. Half the people who read this think one answer is obviously right, and the other half think the other is obviously right.

(Whoops, I've misunderstood you twice. I think my other reply answered the wrong question. Let me try again.)

I didn't say synchronization can prevent EA. I said that calling a finalizer on an application thread can defeat the mutual exclusion that would usually be guaranteed by Java's locking. If the app thread holds a monitor when the finalizer is called, that would usually make the finalizer block until the app exits the monitor. If they're on the same thread, that becomes a recursive monitor enter on the same thread, which doesn't block.

Even if you and I solve this particular problem, these are only the problems that occurred to me off the top of my head. With these kinds of subtle, thorny issues lurking around every corner, I wouldn't expect JIT development teams to give this any real attention unless there's some workload where it matters.

[These are my personal opinions, not those of my employer.]

I must have misunderstood you. You made a statement that I parsed as follows:

"If (this stuff isn't escaping) then (the JIT can prove that it isn't using a lock)."

That is logically equivalent to:

"If not (the JIT can prove that it isn't using a lock) then not (this stuff isn't escaping)."

Hence, I was explaining that using a lock doesn't count as an escape. But apparently I just misunderstood your meaning?

Your argument proves that the time taken can be no less than O(n) in the number of object references in live objects. The (potentially billions of) unreachable objects are never touched.

I agree that limited RAM environments are important. Asymptotic complexity only matters when you're effectively operating at the asymptote.

I seem to have hit a nerve here. Perhaps I haven't been clear about some of the points I'm making, which I don't think are all that contentious.

I'm not saying a fast GC can reduce the cost of allocation. I'm saying a fast GC can reduce GC overhead. I think that's an uncontroversial statement. I can only insist so many times that Java's GC doesn't touch dead objects at any time during its scan. If you want to disagree with me, that is your prerogative.

I agree that a GC walk can thrash cache when it happens. However, a copying GC can also rearrange object layout to improve locality. Which effect is more significant depends on the workload.

I didn't mention the number of cores because it didn't occur to me that it was significant to you. GCs can scale just fine to many cores. No core has to "check" other cores: each core can check its own local data, depending on the tactics employed to deal with NUMA. There are always challenges in scaling any parallel workload, of course, and there are always solutions.

Our GC (in IBM's J9 JVM) uses 8 bits in each object. With classes aligned to 256 bytes, there are eight free bits in each object's class pointer. Hence, J9's object model has one word of overhead on top of the user's data, which is what malloc/free typically needs to track each object's size anyway. No disadvantage there.

[These are my personal opinions, not those of my employer.]

Well, it's hard to deny that the work done by the program is O(n) (or even Ω(n)) in the number of objects created by the program. That's almost tautological.

The thing that is interesting about the asymptotic complexity of garbage collection, though, is that it gives you a peek into the future of GC overhead as heaps get larger.

Consider a program with some bounded steady-state memory consumption, and a GC that takes O(1) time to free unlimited amounts of memory. In such a system, you can reduce your GC overhead arbitrarily simply by enlarging the heap. A larger heap gives a larger time interval between collections, yet doesn't make the collections any slower. Don't like 6% overhead? Fine. Make the heap 10x larger and you have 0.6% overhead.

This time-space trade-off is always available to the end user of such a system. It's certainly not available with more primitive systems like malloc+free.

[These are my personal opinions, not those of my employer.]

The JVM doesn't release the memory to the OS when garbage is collected; only when the heap shrinks. Any zeroing the OS might do is proportional to the size change in the heap, not to the number of objects freed.

I think you're referring to a mark and sweep collector. Java's garbage collector uses better algorithms. The collector never even looks at objects that are not referenced from the root set, and an allocation operation is nothing but a pointer bump. Even Cheney's algorithm from the early 1970s works this way, and collectors have only improved since then.

"Go compiles to native code. Java, ultimately, does too, but compilation happens at runtime, which is an overhead that Go doesn’t have, so in principle Go should be faster."

Oh boy. It pains me every time I hear this.

In a microbenchmark, JIT compilation increases the time to ramp up to peak performance. Once you're at peak performance, compilation time doesn't matter anymore. At that point, the JIT compiler's drawbacks are mitigated by two distinct advantages.

First, the JIT has information about run-time conditions that was unavailable to the static compiler. It's hard to over-state how important it is to know, for example, which if-statements are highly biased, or which polymorphic calls are effectively monomorphic at run time.

Second, the JIT can make over-aggressive optimizations based on unverified assumptions, secure in the knowledge that when an assumption proves to be wrong, it can discard that code and re-compile another version of the same method, optimized according to the latest available information. This is only possible if the compiler is present at run-time.

[These are my personal opinions, not those of my employer.]

It's hard to say. The finalization spec is subtle.

To make this work, the JIT would need to perform escape analysis on the finalizer too (since the finalizer can make an object escape). If that analysis succeeds, then I suppose the object could be stack-allocated and the finalizer called directly. Even then, though, you would have to think carefully about all the subtle ramifications, like the consequences running a finalizer on an application thread instead of the finalizer thread. (What if the finalizer grabs a lock in an attempt to protect itself from the application code? Now this becomes a recursive lock by one thread, and so both the application code and the finalizer can enter it at the same time!)

If this ever became important enough, it's possible the JIT developers could get this right in every case with enough effort invested. I wouldn't count on that happening any time soon.

[These are my personal opinions, not those of my employer.]

Me too. I would fully expect the JIT compiler to remove this call unless it's more complicated than it sounds. I don't think this is a real fix.

I work on IBM's JIT compiler. We had to do something similar to fix this problem in DirectByteBuffer a few years ago, by adding a call to a magic method called keepAlive. To make this work, we had to teach the JIT to handle this method specially, or else it would have ripped it out, and the fix would have had no effect.

[These are my personal opinions, not those of my employer.]

"I wish that when GC encountered a cycle it would just tell the programmer that they now have a leak..."

Wow. Overreact much? :-)

Garbage collection works great for one use case: managing heap memory. Please, let's not return to a universe where you need to free each chunk of memory piecewise. That makes freeing memory O(n) in the number of chunks freed, where modern collectors can free unlimited numbers of objects in O(1) time. Compared to this, malloc and free are a little like keeping score in soccer by tracking every action that does not result in a ball entering a net.

The trouble is finalizers. Finalizers suck. They tie the management of one resource (say native memory or file handles) to an unrelated resource (heap memory). This has the terrible consequences highlighted by this story, plus others. It keeps the native resource tied up until the collector decides to run, for one thing. It also harms performance in secondary ways even when the finalizer isn't running. For example, finalizers typically defeat escape analysis, meaning objects with finalizers can't be stack-allocated.

It's a good idea to use finalizers mostly for detecting invalid final states of objects (like leaking resources), as long as you stay aware that finalizers can slow down your program in unexpected ways.

[These are my personal opinions, not those of my employer.]