Argument scope

B

BGB

I can not imagine anyone suggesting a struct for Java with the
semantics of never being in the heap.

It has probably been suggested to get struct for Java with
the C# semantics of being a value type.

yeah, this is what I want...


granted, I also want them to be supported at the VM level, rather than
via compiler hackery and using multiple signatures (this is what John
Rose's blog had mentioned).

granted, I would likely leave control of the semantics to the bytecode,
with restrictions placed on how they can be used (this being the likely
"path of least impact" WRT the VM design).


I may not bother at the moment with a "standard JVM friendly" version.
if it ever matters a standard JVM version can be beaten together later.

sadly, initially I would probably end up using the considered internal
interface directly (since I don't yet have a working compiler for Java...).


or such...
 
B

BGB

Thanks, Arne, but if I ever care again, I will manage to find where to
look it up :)

yeah, in C++ the bigger difference is whether or not one passes the
thing directly or via a pointer than whether or not one uses a class or
a struct...

since Java lacks pointers (or, more specifically, lacks the absence of
pointers...) there is a hole left here.

C# then partly re-interpreted the use of the struct concept, and
assigned it to this use case.

thus...
 
T

Tom Anderson

The above analysis ignores the fact that, if one conflates "value type"
with "stack-allocated", then value types can wind up having _longer_
lifetimes than reference types. In particular, they won't be released
until the stack frame in which they were allocated is removed.

Why can't an aggressive compiler reuse the stack space used for a
stack-allocated object once it's no longer live, before the whole frame is
popped?

I think it can do this at the point the variable holding the object in the
method execution owning that frame dies; the point at which a
heap-allocated version of the object becomes dead cannot be any earlier
than that.

tom
 
T

Tom Anderson

Pascal had it already.

You snipped the bit where i said we were stealing this from Delphi!
I learnt Pascal before I got in touch with C, and bemourned the lack of
"with" in C. I'd love to finally see it at least in Java :)

I'm now swinging back against it:

http://stackoverflow.com/questions/514482/is-delphi-with-keyword-a-bad-practice

Although i see Visual Basic has a version which combines Pascal's 'with'
with my idea about leading full stops:

http://stackoverflow.com/questions/71419/whats-wrong-with-delphis-with/71470#71470

So you write:

with (out) {
.write("foo");
.newLine();
.write("bar");
.newLine();
}

This is better than the Smalltalk ripoff, because it makes the receiver
clearm and it's better than the Pascal ripoff, because it defeats the
error where things unexpectedly come into scope, mentioned in the first
StackOverflow link above.
semantic meanings are overrated :)

PS: there's also:
BufferedWriter _ = ...;
_.doThis(); _.doThat(); ...
or, using short names like "b", or "bw".

GET OUT.

tom
 
T

Tom Anderson

Currently, in the present, likely on your very computer, the
(commercial) JVM (at least in "-server" mode) will inline some things,
enregister others, and convert references into stack-based primitive
values. It's already doing the implementation strategies that have been
mentioned here.

But only where it can prove by escape analysis that it is safe to do so.
Having value types would allow it to prove that of much more code.

tom
 
A

Andreas Leitgeb

Tom Anderson said:
You snipped the bit where i said we were stealing this from Delphi!

Pascal predates Delphi (either as a lang or just as a name, dunno)
I wrote "already", so I meant: even before there was "Delphi"
(ignoring the ancient place Delphi and its oracles for now).

That's just opinions. Some of them appear to be due to implementation
issues of the BDS, others are (as I see it) expressions of personal taste.
The problem of added fields causing confusion isn't principially different
from Java's 'import's with wildcard, only more local.

Ok ;)
 
L

Lew

Andreas said:
Pascal predates Delphi (either as a lang or just as a name, dunno)
I wrote "already", so I meant: even before there was "Delphi"
(ignoring the ancient place Delphi and its oracles for now).

Delphi (1995) *is* Pascal! Well, Object Pascal (1985), anyway.

Pascal came out in 1970. When was Borland founded? (Hint: August,
1981).
 
A

Andreas Leitgeb

Lew said:
Delphi (1995) *is* Pascal! Well, Object Pascal (1985), anyway.
Pascal came out in 1970. When was Borland founded? (Hint: August,
1981).

Anyway, unless Borland introduced "with" into Pascal (which I don't think,
but also don't care enough, either, to check), Borland's entering the scene
and appearance of the name delphi later seem entirely irrelevant to the
history of "with". And the history of "with" itself is really irrelevant,
too, here ...
 
B

BGB

Right. In C++, there's really no difference between a class and struct,
other than default accessibility. But then, C++ doesn't really have
reference types at all (that is, types that are _only_ accessibly via a
pointer, reference, or whatever you want to call it). Everything's
essentially a value type, which you can then treat as reference types by
passing pointer values instead of the actual value itself.


I'd say C# completely re-interpreted it. :) Rather than embedding the
value- or reference-type aspect in the variables, in C# (or more
specifically, in .NET…this isn't unique to C#) it's embedded in the type
system itself.

yes.


well, I went and implemented the idea as I imagined it into my VM.
in the simple form I was imagining, this was actually a fairly quick and
simple alteration...

for a proper JVM, it might be a little more effort, since JNI doesn't
export the needed methods, and one doesn't have direct access to the VM
internals (in my case, I side-stepped JNI some in order to implement it).

my object system already had 'clone', so I mostly just went and added
support for 'free' (this was needed as objects exist as multiple parts
in memory, so simply telling the GC to free the object-handle will not
work correctly). also, 'set' needed to be added (likewise).

note: they are multi-part in memory such that physical layout may be
changed in existing objects without losing object identity (IOW: their
base address). prototype objects may contain potentially more pieces
(explicit field and method lists).

'set' was similar, except 'set' doesn't currently bother with any
prototype state (any prototype state is ignored). set will fail if they
don't have the same physical / in-memory layout (different base classes
or different class versions).

but, no big deal...


decided to leave out a more technical description of the OO machinery
and parts of how the Prototype-OO machinery works (neither Java nor
structs use P-OO features anyways, so not much direct relevance...).


note that, in this case, a C-like struct may be gained by fetching the
object's data area, which basically just has the fields packed
end-to-end in memory (respecting proper alignment rules, ...).

or such...
 
B

BGB

[...]
The above analysis ignores the fact that, if one conflates "value
type" with "stack-allocated", then value types can wind up having
_longer_ lifetimes than reference types. In particular, they won't be
released until the stack frame in which they were allocated is removed.

Why can't an aggressive compiler reuse the stack space used for a
stack-allocated object once it's no longer live, before the whole frame
is popped?

I've never seen a compiler that reuses local variable slots _across_
method calls. Doing so within a given method call is fine and reasonably
common AFAIK, but it doesn't change the fact that that stack slot is
still allocated until the method where it was used returns.

And I'm skeptical that it's feasible to reuse the stack slot when some
callee of the method owning that frame hasn't returned yet. How is the
compiler supposed to know when it generates the code for some other
method that the method where the stack slot to be reused will always be
present in the call chain, and always in the same location?

I would much rather see the compiler writer spend more effort on
determining "definite assignment" than attempt to solve the static
analysis problem that's required to accomplish what you suggest across
method calls. And in any case, even if it could be accomplished in some
scenarios, it's not possible to always guarantee the conditions that
would be required to reuse the stack slot. So the fact that the
optimization might occur some of the time doesn't change the fact that
making something a value type can't ensure it can occur _all_ of the time.

Hence, "value type" is not a suitable way to describe object lifetime.
Which is my point. It can correlate with object lifetime, but the real
reason to have value types has nothing to do with object lifetime.
I think it can do this at the point the variable holding the object in
the method execution owning that frame dies; the point at which a
heap-allocated version of the object becomes dead cannot be any earlier
than that.

The heap-allocated version of the object can be released from the heap
at that point though, regardless of the current depth of call stack or
where the execution pointer is.

Even if it's practical to write a compiler as aggressive with
optimizations as you suggest, that stack slot is still present for
_something_. What happens when _none_ of the variables that were sharing
that stack slot are no longer needed? Unlike with the heap-allocated
object, that stack slot remains and cannot be freed until the method
where it was declared has returned.

Again, "value type" isn't a reliable way to describe object lifetime,
nor is that IMHO something that the author of managed code (such as Java
or .NET code) should be thinking about anyway. If you want that kind of
fine-grained control over object lifetime, I don't think a language that
uses garbage collection is really the best choice in the first place
(with the corollary being that most people over-estimate their need to
control object lifetime, given a sophisticated enough garbage
collection, such as found in Java or .NET).

I disagree here in the sense that it matters...

exact object life-time on the stack is much less important than on the
heap, since one will never need to GC the stack (if it ever runs out,
this is a stack overflow, not a GC...). the closest direct analogue of
stack-based GC I am aware of is optimizing for tail calls.

however, if one has a naive VM implementation which is leaking garbage
onto the heap, then knowing that an object is dead may allow freeing it
earlier, delaying the inevitable GC pass, and its resultant potential
interruption of the user experience.


a related case is with VM's which support continuations and lexical
closures, where the ability to prove that no continuations are made, and
that no closures are captured, may have a notable effect:
suddenly, the VM is no longer spewing out old function call-frames and
local-binding scopes as heap garbage, and suddenly overall performance
is a bit better...


so, alas, it is partly a matter of semantics, and partly a matter of
ability to optimize it.

whether or not it represents explicit control or not is another issue,
but it can be taken as a big hint...


granted, my VM technically has no particular restriction that structs
can't be inherited (similar to classes), but C# does impose this
restriction, and it seems to make sense for how they are used: as small
value types.


an example use of where something like this would be really nifty:
struct Pointer
{
public long address;
}

now one has an alternative to using raw long's to represent native
machine pointers (and dealing with the issue that method overloading no
longer knows if one means to pass a large integer value, or a machine
pointer), and also using classes (and dealing with pointer-heavy code
spewing garbage).

granted, it is almost tempting to support inheritance for sake of
having, say, VoidPointer and BytePointer and similar, but nevermind this...


or such...
 
L

Lew

yes, having an optimization is one thing, proving where one can safely
use it is another.

a struct need not be "that" much different than a class WRT how the VM
handles it (nevermind the semantic differences), but it does give a very
big hint to an optimizer that it may safely put the thing on the stack,
even when passed around, since it knows that no references to it may
ever be directly captured (only ever copies of it).


this is why, for example, C has some number of little keywords which may
be ignored by the compiler, but may also provide helpful optimizer hints.

it is because, otherwise, there are things that a human programmer may
realize but are beyond the ability of an optimizer to safely infer.


a hint like:
"unless I return the thing, its lifetime WILL end at the end of this
function scope" is a bit stronger than, say, "well, if you can safely
prove that this object is not captured anywhere, then its lifetime may
end at the end of the function scope".

it is in-fact such a big hint that a naive VM can just do it and still
get it right.

Right now the Hotspot compiler recognizes when a particular set of usages is
safe to optimize, say to place on the stack, even when it might not be later.
So it dynamically will optimize when it can, and de-optimize when the
situation changes. So Hotspot can invoke these optimizations even when static
analysis would indicate them as unsafe.

Changing the whole language because you think (without hard evidence) that
value types will have better performance is premature optimization, Knuth's
root of all evil.

Provide a rationale based on the semantics, not on some unproven prediction of
some unknowable degree of optimization that you imagine will happen.
 
A

Arne Vajhøj

Anyway, unless Borland introduced "with" into Pascal (which I don't think,
but also don't care enough, either, to check), Borland's entering the scene
and appearance of the name delphi later seem entirely irrelevant to the
history of "with". And the history of "with" itself is really irrelevant,
too, here ...

WITH is standard Pascal (both Wirth and ISO).

Arne
 
B

BGB

Right now the Hotspot compiler recognizes when a particular set of
usages is safe to optimize, say to place on the stack, even when it
might not be later. So it dynamically will optimize when it can, and
de-optimize when the situation changes. So Hotspot can invoke these
optimizations even when static analysis would indicate them as unsafe.

yes, but I am not using HotSpot, and the particular way this feature has
been implemented in my case should be possible to fake on a standard
JVM, and via JNI, although likely with a performance hit.

also possible is to use a pure-Java strategy as well, where the
semantics are simply faked.

however, since I am not using a standard JVM for this, it really doesn't
matter whether or not a standard JVM can figure it out or not, if mine
can't, really this is what is important in this case.

Changing the whole language because you think (without hard evidence)
that value types will have better performance is premature optimization,
Knuth's root of all evil.

the ability to optimize the standard case is beyond the abilities of
*MY* VM, hence this feature is needed, what if anything is done in
standard Java-land is irrelevant.

more so, the feature is needed for C# support to work (I am supporting
this in addition to Java, although both will use a JBC-based
implementation), so may as well add it.

also, it is needed to correctly support BGBScript, as well as the need
for Prototype OO (this is already supported by the VM, although I have
no current plans to support this feature directly from Java).


additionally:
as it stands, I don't actually need to change the language to implement
this, as the implementation is accessible without using any language
extensions, although less cleanly.

class Foo extends Struct
{
public double x, y;

public Foo() {}
}

....
Foo x=new Foo();
Foo y;

y=x._Clone();

barFunc(x._Clone());
x._Free(); y._Free();

this is nasty, but little stops it from being used.
for now, this is sufficient.

Provide a rationale based on the semantics, not on some unproven
prediction of some unknowable degree of optimization that you imagine
will happen.

well, how about we disregard whatever HotSpot can or can't do, and
consider it this way...


it is like, one can also claim that an object is immediately known to be
no longer accessible when all references disappear if one uses
ref-counting, but if one can't use ref-counting for some reason, well
then, this is no longer a good option.

same sort of issue here...


also, the same VM is planned to be used for several other languages
which do need a feature like this to be implemented, so arguing against
it on these grounds is moot...

if I use C with the VM, also some means of handling pointers is needed,
and the main options either include adding a new value type, or
alternatively, hacking pointer support into the core VM, or just plain
boxing the pointers...

so, alas, one can take their pick as to the least horrid options...
 
A

Arne Vajhøj

Changing the whole language because you think (without hard evidence)
that value types will have better performance is premature optimization,
Knuth's root of all evil.

Provide a rationale based on the semantics, not on some unproven
prediction of some unknowable degree of optimization that you imagine
will happen.

Given that the problem has been occasionally demonstrated here,
then I find it hard to consider premature.

Arne
 
B

BGB

Given that the problem has been occasionally demonstrated here,
then I find it hard to consider premature.

well, as well in that a lot of stuff just seems awkward without
something like this, regardless of whether or not it is faster...

the inability to declare new value types is just constraining.

like, in C (or C++), it is common just to use 'typedef' or similar to
redefine old types as new names such that one can create a new
conceptual type.

for example, it is not just an integer, it is a "typeHandle"...
and, a instance object behaves little like a raw integer or similar.

struct-based value-types serve a similar role, and can be used to
introduce "new" conceptual base types.

Java then has no direct analogue of this notion.


or such...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top