Skip to main content

Extended primitives

38 replies [Last post]
spiff
Offline
Joined: 2003-06-10
Points: 0

Small objects are expensive in Java. There is a memory cost of 8 bytes per object plus 8-byte alignment in HotSpot. This makes, for example, a Vector3f (float x,y,z) consume 24 bytes of memory in Java (8 + 4 + 4 + 4 + 4(padding)) where it would only consume 12 bytes in C/C++. There is also a related performance cost. If you allocate, for example, a Triangle with 3 Vector3f's, you have to do 4 new's (one for the Triangle and 3 for the Vector3f's) and new's can be particularly expensive (not so much on single processor machines, but definitely on multi-processor machines).

One solution to this is Java primitives (int, byte, float, etc.) Primitives have their lifetimes bound to their containing object or stack frame and therefore require no overhead memory-wise for the garbage collector. They also cannot have references made to them and so introduce no additional work for the garbage collector.

What I am asking for is an "extended primitive". That is, something that acts in every way like a primitive, but that can have user-defined fields within it. Such extended primitives would not be part of any object hierarchy, there would be no way to get a reference to them, they would be passed and copied by value. They would be used something like this:

extprim vector3f {
float x;
float y;
float z;
}

class Triangle {
vector3f v0;
vector3f v1;
vector3f v2;
}

This would allow the types of small objects that didn't quite make the "primitive cut" to not incur the huge penalties that they currently do. For example, a Complex object with float real and imaginary fields should consume 8 bytes; it currently occupies 16 bytes. The "double" primitive on the other hand consumes just 8 bytes as it should.

This feature is slightly more restricted than full-blown structs. I don't want references to extended primitives and I don't want them to be part of an inheritance hierarchy. I just want the other small objects to get the same treatment as primitives.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
markf
Offline
Joined: 2005-01-20
Points: 0

http://citeseer.ist.psu.edu/vanreeuwijk02adding.html

Reading through this, it looks ideal. It's not overly ambitious like Kava, and would fit in well with the existing language.

If there was a page to vote for new features, C. Van Reeuwijk's proposal would get my vote.

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

I have been think more about extended primitives and I think it would be possible to implement a fairly restricted form of them by modifying a compiler to automatically inline classes (ie class-erasure in analogy to the type-erasure of Java's generics implementation). With the closing of the multiple-return-values request, it is clear that Sun currently is mostly interested small syntatic tweaks for ease of programming, and not language changes for improved performance or memory efficiency. We need more hard data to convince them otherwise.

The modified compiler could recognize certain classes (eg subclasses of a special JavaExtendedPrimitive class) and inline them out of existence at compile time. With enough restrictions on how they can be used, it may even be possible to guarantee that the modified programs will compile and run both using standard java compiler and using the modified compiler. This would allow performance comparisons and mean you could still run your modified code even if the special compiler went away.

I took a compiler course in grad school, but I'm not a compiler expert. I'm looking for a compiler or similar tool (or code refactoring engine?) that I could modify to force it to inline classes at compile time. I looked at the source for javac, but since it is largely undocumented, I haven't been able to understand it enough to modify it. The apt tool does not seem appropriate since the source of every class that uses/references an extended primitive has to be modified. Is there some other compiler or tool out there that I should look at? Or maybe a compiler enthusiast/student who would be interested in such a project?

sjasja
Offline
Joined: 2004-08-15
Points: 0

> We need more hard data to convince them otherwise.

You might want to rephrase that slightly, lest someone reads it as "I have made up my mind, and will now cook up some evidence to support it" :-)

Check out existing research, such as http://portal.acm.org/citation.cfm?id=320386 (google will find more of the same; the magic words are "escape analysis").

> (eg subclasses of a special JavaExtendedPrimitive class)

Such an approach quickly comes inconvenient, as you start to need PrimitiveStringBuffer, PrimitiveInteger, PrimitiveLinkedList, ...

And not having the "object header" breaks things like polymorphism.

> I looked at the source for javac, ...

This isn't much of a javac issue, it's more of a JIT/dynamic compiler thing. You'd need to dive deep into Hotstpot. Good luck with that...

Inventing new language structures for this is probably (and hopefully) not going to happen. There is a much much much better way to do most of "small quick structs" in a way that doesn't break a lot of the language, and makes it better than any "fairly restricted" hack. And that way may be one of the flying cars in Mustang: http://www.google.com/search?q=%22escape+analysis%22+mustang

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

Thanks for the pointers, definitely some interesting reading. I hope that escape analysis does become a standard part of the JVMs. But even if that happens, it won't solve the most of the problem that I'd like to see solved through extended primitives.

What I most want is to be able to create more complex objects by composing smaller entities (objects or extended primitives) without suffering the memory overheads currently associated with small objects. These complex objects are mostly long lived, (ie they escape any reasonable scope), so escape analysis is not a solution. What I primarily want is the direct embeding of extended primitives inside objects, not stack allocation of objects, which is just a nice side-benefit.

For example, a triangle object might contain three vertices (Vector3f) and a color. That is four extra/unnecessary object references and headers (4X(4+8) = 48 bytes) of extra memory overhead if I use object composition. If I have millions of triangles, this quickly adds up to a very large overhead. If I inline the data fields directly into the triangle object, I can save all that space, but I lose much of the abstraction, end up duplicating code, and make the code harder to read and maintain.

I understand from a pure ease-of-programming point of view, it is convenient if you can make everything an object (if all the world looks like a nail, then a hammer in the only tool you will ever need), but there is a cost to this approach (otherwise Java would never have had primitives in the first place). It would be nice to give programmers another tool to use in cases when they think the object overhead is too high. Since Java already has primitives, it is not a radical change to the language to support extended ones, though not a trivial one either.

I take some offense at the suggestion that I might be suspected of cooking up data to support my position. I do believe that I'm right, but I am willing to be proven wrong (or more likely ignored unless I can show more evidence). There is some preliminary supporting data already in this thread for extended primitives, but clearly it has not convinced many people. It would be much more convincing if extended primitives could be tested in a more realistic program and not just a microbenchmark. Hence my suggestion to create a modified compiler to do just that.

leto293
Offline
Joined: 2005-05-13
Points: 0

For my two pence worth, I think a lot of the requirement for adding new primitives comes from games developers, who feel the pain of vector churn in an environment that forces them to make every complex scalar type a class.

With regards to your idea of creating a modified compiler, I see no need. You can benchmark the use of Vector3f as a class against Vector3f as a struct by using C#, which has supported the struct construct since its inception. I would be very interested to see such a comparison.

josgood
Offline
Joined: 2003-06-18
Points: 0

Extended primitives is a great idea. It's also been called "lightweight objects".

Kava: A Java dialect with a uniform object model for lightweight classes (2001)
http://citeseer.ist.psu.edu/bacon01kava.html

Kava even sports generics. Without autoboxing.

Were this a perfect world, Kava would have become JDK 1.5 "Tiger". Versus whatever it is we have now. (Shout out to Gilad Bracha! Thanks buddy! Good job!)

A language construct for tuples is also very useful. It would enable all those Java Grande applications.

Adding Tuples to Java: a Study in Lightweight Data Structures (2002)
http://citeseer.ist.psu.edu/vanreeuwijk02adding.html

Just quick word on stack allocated objects: Don't!

IBM and others have been refining escape analysis techniques. That's when the JIT figures out that an object can be safely allocated on the stack (vs the heap) by determining that there are no external references to that instance. It's a bit like the automatic removal of unnecessary synchronization.

As with automatic garbage collection, the computer can usually do a much better job of it than you can.

It's sad, really, that Microsoft added that feature to C#. Probably just to be "different". Very smart people have already thought through all this stuff. It'd be to our advantage to learn from their efforts.

hlovatt
Offline
Joined: 2003-11-18
Points: 0

You can also download the Pattern Enforcing Compiler (PEC)

https://pec.dev.java.net/

Particularly the compile API:

https://pec.dev.java.net/nonav/compile/index.html

As the name suggests a PEC enforces patterns. The relevant patterns are Immutable:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

Value:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/value/Value.html

and ImmutableValueConversions:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

You can also write your own patterns.

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

I think you are missing the point of extended primitives. They are not objects and have somewhat different semantics (eg pass-by-reference vs pass-by-value). What they gain is efficiency, not base functionality. Anything that can be done with extended primitives can be done with objects instead; its just often slower and requires more memory. Escape analysis can be used to gain this efficiency in certain limited cases, but is not possible in many others. The differences in semantics and memory organization can only be hidden by the compiler when they are limited to a very small scope (eg, provably only used within one method and thread).

hlovatt
Offline
Joined: 2003-11-18
Points: 0

The immutables in the pattern enforcing compiler (PEC)

https://pec.dev.java.net/nonav/compile/index.html

are designed to enable a JVM to eliminate the memory overhead, see:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4617197

The immutable classes are marked as immutable by implementing the interface Immutable therefore if the JVM wants to inline them it can (provided the class is also final or effectively final). When it inlines them the JVM:

1. Doesn't need the pointer to them - it has inlined them whether this be on a stack, inside a structure, or in an array

2. Doesn't need the extra word associated with garbage collection and synchronization since it is inlined and also immutable and therefore no point in implementing synchnization or garbage collection for them

3. Doesn't need the extra word associated with method dispatch because the class is final or effectively final and therefore the JVM can inline method calls

Therefore the immutable as implemented by the PEC can be inlined by the JVM. However I have only written the compiler I haven't written a modified JVM that inlines.

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

Its true that that at least in some cases a compiler can inline immutables, though there are a number of tricky cases such as:

- atomicity of update (true when swapping a reference to an immutable objects, not true when updating a compound extended primitive)
- the immutable class must be final. Part of extended primitives efficiency comes because the compiler always staticly know their type unlike objects. If an subclass of complex number might be assigned to a complex number reference, then inlining cannot be used even if the classes are immutable
- arrays, collections etc. Immutable objects can be put in object arrays and collections while extended primitives cannot. I guess it might be made to look somewhat like autoboxing, but I'd much prefer arrays of extended primitives to look like primitive arrays, not arrays of objects (ie without the extra indirection and object overhead).
- I'll have to think more to see if there are other problem cases

The other problem of course is that if the JVM does not aggressively inline them, then using immutables actually make the object creation overhead and efficiency much worse. The immutable pattern is definitely useful and can really help in code readability and thread robustness. But its not clear to me that its a path to gaining the performance and efficiency gains that extended primitives would achieve.

markf
Offline
Joined: 2005-01-20
Points: 0

It sounds almost like you want tuples of primitives. I think that, properly implemented, tuples provide exactly what you're talking about. Picture the following:

[code]
// Initializing tuples.
(int,int) point = (0, 0);
(float,float) e_to_i = (0.54, 0.84);
(int,String) address = (7165, "Pandora Street");

// Doing some geometry on points.
(double,double) p1 = (7.8, 5.5);
(double,double) p2 = (10.2,13.1);

double distance = hypotenuse(p1, p2);
double direction = atan2(p1, p2);

// Tuple assignment, just like in Python.
// norm_theta returns a (double,double) tuple.
double dis, dir;
(dis, dir) = norm_theta(p1, p2);

[/code]

After using Prolog, Haskell, and Python, I got pretty attached to tuples and tuple-notation. It's one of the things that I really miss when I use Java.

jesse_beaumont
Offline
Joined: 2003-06-14
Points: 0

Looking at your code I am not sure this warrants a new language construct, it seems. Surely a Tuple class with generics support would come quite close to this (though admittedly with a bit more verbosity)

[pre]
// Initializing tuples.
Tuple point = new Tuple(0, 0);
Tuple e_to_i = new Tuple(0.54, 0.84);
Tuple address = new Tuple(7165, "Pandora Street");

// Doing some geometry on points.
Tuple p1 = new Tuple(7.8, 5.5);
Tuple p2 = new Tuple(10.2,13.1);

double distance = hypotenuse(p1, p2);
double direction = atan2(p1, p2);

double dis, dir;
Tuple tup = norm_theta(p1, p2);
dis = tup.get(0);
dir = tup.get(1);
[/pre]

J.

markf
Offline
Joined: 2005-01-20
Points: 0

> Looking at your code I am not sure this warrants a
> new language construct, it seems. Surely a Tuple
> class with generics support would come quite close to
> this (though admittedly with a bit more verbosity)

a) A generic-class implementation of tuples doesn't improve on space or speed at all. It would just be syntactic sugar.

b) A generic-class implementation is impossible, because you can't have a variable number of parameters. I actually have some classes like this, called Pair and Triplet respectively. They're great for testing. By using a factory, they don't even need to have their parameters specified -- the generic inference system does it all for you. They're awesome, but still no substitute for real Tuples.

The idea behind a Tuple is that it's a primitive -- and is passed by value, can be easily made immutable with the final keyword, and so on. The difference between a primitive Tuple and my Pair/Triplet classes is analogous to the difference between an array of ints and an ArrayList of Integers. You can see why this would be good.

A quick look at the ridiculous number of packages that have been developed to provide implementations of the Collections API for primitive types demonstrates how important a robust set of primitives is. And Tuples would extend the power and flexibility of the existing Java primitives substantially.

hlovatt
Offline
Joined: 2003-11-18
Points: 0

See the tuple request for enhancement for a discussion of tuples:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4222792

In particular take a look at the proposed Tuple API suggested in that discussion that does enable primitives and also look at what people are saying about using tuples in public interfaces. To summarize the against position:

1. You can use a Tuple API
2. Do you really want (double, double) instead of Complex?

hlovatt
Offline
Joined: 2003-11-18
Points: 0

The following compiler allows immutable types:

https://pec.dev.java.net/nonav/compile/index.html

and does not involve any new syntax. A JVM that recognised the immutable types could inline them. Therefore I think this feature can be added without major changes to Java.

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

Immutables seems like an orthogonal idea to extended primitives to me. Immutables could be auto-inlined, but that is only a good idea if they are not widely shared (many references to the same immutable). Extended primitives are designed to make small entities (like complex numbers) as efficient as primitives (like int or float) are now. Immutable (final) ints are sometimes useful but frequently it is better to allow them to be modifiable.

hlovatt
Offline
Joined: 2003-11-18
Points: 0

The Pattern Enforcing Compiler (PEC) enforces patterns as the name suggests. The relevant patterns are Immutable:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

Value:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/value/Value.html

and ImmutableValueConversions:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

You can use these three patterns to achieve extended primitives, see integer example:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

Particularly section that begins "A typical development cycle".

A problem with allowing anyone to extend primitives is how they will interact with each other, i.e. will your double work well with my complex. This issue is addressed by the multiple dispatch pattern:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/multipledispa...

Particularly the "Numeric Example" section.

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

Perhaps it might help the discussion if we looked at what would have to be changed in Java (compiler and/or JVM) to support extended primitives. Suppose we declared complex numbers as an extended primitive as follows

extended_primitive ComplexNumber {
double realPart;
double imaginaryPart;

public void add(Complex toAdd) {
realPart += toAdd.realPart;
imaginaryPart += toAdd.imaginaryPart;
}
...
}

and some object that contains a complex number such as:

class WaveFunction {
ComplexNumber phase;
...
}

Directly embedding the ComplexNumber into WaveFunction could be handled by the compiler substituting two double fields into the WaveFunction object (phase_realPart and phase_imaginaryPart). Similarly extended primitives as function arguments (such as ComplexNumber.add()) could be handled by substituting the extended primitives constituent fields (internally add would take two arguments of type double). However there are other common uses that are not so simple such as:

1) [u]Returning an extended-primitive from a function[/u]. Currently Java has no clean way to return multiple values from a function (though many would like it to, as shown by RFE #4222792). It could be accomplished by allocating a hidden class to encapsulate the values until a better way is added or found.

2) [u]Arrays of extended-primitives[/u]. ComplexNumbers could be handled because they contain only one primitive type (double) and so could use a double array as backing store, but extended-primitives containing more than one type would be more difficult. They could be handled using ByteBuffer as long as they do not contain object references.

3) [u]Type equivalence[/u]. Although they could be thought of as simply tuples, we may want stronger typing. For example, we probably want a ComplexNumber to be a different type than a 2D point even if both consist of two doubles.

4) [u]Autoboxing/unboxing[/u]. While I'm not personally a fan of this, now that it has been added to the language, programmers will probably expect it. Unfortunately this means that every extended-primitive will also have to implicitly declare a corresponding Object class with the same fields.

5) [u]Reflection[/u]. Once added extended primitives would have to be supported in reflection which would certainly mean added some new methods (and likely classes) to support them.

I think these are the major issues that would have to be addressed, though there may be others that I'm not remembering at the moment. However I think all of these issues could be addressed and extended primitives added without having to change the language very much.

jwenting
Offline
Joined: 2003-12-02
Points: 0

no way. You're destroying the entire OO concept, turning Java into C (which seems to be what a lot of the things here seem to want, probably thinking that Java should be C).

Let's also declare a List, Map, Array, and a lot of other things all as primitives.
Maybe introduce pointers so you can change the memory address that a method parameter refers to.
Maybe I'd like to do something like

void somemethod(SomeStruct* s)
{
SomeStruct* t;
s = t;
}

and have the memory address s refers to change outside the function...

spiff
Offline
Joined: 2003-06-10
Points: 0

This does not "destroy the entire OO concept" anymore than having ints, floats and doubles does. Primitives in Java already have the behavior we're discussing for Extended Primitives. All this proposal does is extend this behavior to other domain-specific entities (such as vector3f and complex) that didn't quite make the small-and-frequently-used-enough-to-be-a-primitive cut.

I believe that their limited functionality would prevent them from being abused. For example, since you can never get a reference or pointer to an extended primitive, you can't declare list nodes as extended primitives. They also would not have methods or participate in inheritance so I don't see people rushing to turn a List class into an extended primitive. Limiting their functionality is key to making sure they're not abused.

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> This does not "destroy the entire OO concept" anymore
> than having ints, floats and doubles does. Primitives
> in Java already have the behavior we're discussing
> for Extended Primitives. All this proposal does is
> extend this behavior to other domain-specific
> entities (such as vector3f and complex) that didn't
> quite make the
> small-and-frequently-used-enough-to-be-a-primitive
> cut.
>
> I believe that their limited functionality would
> prevent them from being abused. For example, since
> you can never get a reference or pointer to an
> extended primitive, you can't declare list nodes as
> extended primitives. They also would not have methods
> or participate in inheritance so I don't see people
> rushing to turn a List class into an extended
> primitive. Limiting their functionality is key to
> making sure they're not abused.

Not untill you add an emulsifier such as auto-boxiing ;)

I think that there should be a mechanisum in place to extend "primitives". To bad that primitives are exposed as primitives otherwise it might make this easier.

Should this support be a change in the langauge or could there be native support for it (JNI perhaps???)

sebastianf
Offline
Joined: 2003-06-13
Points: 0

Hi, I've started a thread over at javalobby to discuss the different possible implementations of structs/extprims to find some common ground between the different proposals I've seen.

I think structs are important and it would be good to get things moving by uniting all developers interested in structs/extprims for Java in a single thread. I'm inviting other developers that have suggested similar implemantions to participate too.

Thanks,

Sebastian Ferreyra

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

Unfortunately the JavaLobby thread demonstrates the general confusion about what extended primitives are and are not.

They are NOT:
- A solution for easily reading/writing structured hunks of memory for communicating with code outside of Java (eg, Javolutions Union/Struct, CAS's "structs" as proposed in RFE #4820062, or java.nio.ByteBuffers). This would require the primitive's components to reside at prespecified memory offsets, which is not a good idea. The JVM should be free choose their layout in memory to optimize for platform specific alignment issues.

- A solution for stack allocation of objects. Although extended primitives would be stack allocated when used as local variables just as primitives are now, extended primitives are not Objects (ie not subclasses of java.lang.Object). You cannot get a reference to one and they do not inherit Objects methods (eg, no wait(), hashCode(), or finalize()). Stack allocation of Objects requires escape analysis which may be added someday, but is a completely separate issue.

They are:
- A solution for entities perhaps should have been included in Java's set of primitive types but were not. Usually because they are only needed in particular application subdomains (eg, complex numbers or 3D points). Because Java provides no way to extend its set of primitives, programmers resort to encoding these as small objects currently. However the memory (and performance) overhead for small objects can be quite high and they could be more cleanly and efficiently represented as extended primitives if Java allowed them.

kersam
Offline
Joined: 2004-11-18
Points: 0

I've made some benchmarks:

public class/struct Complex {
private double re;
private double im;

public Complex(double re, double im) {...}

public Complex add(Complex c2) {
return new Complex(re + c2.re, im + c2.im);
}

...
}

Complex[] c = new Complex[len];
for (int i = 0; i < len; i++)
c[i] = new Complex(i, i);
for (int i = 0; i < len; i++)
c[i] = c[i].add(c[len -1 - i]);

Testet with Java (class) and C# (struct):
Java 800ms, C# 160ms
C# with class: 900-1000ms

Compare the "manual" version:

double[] re = new double[len];
double[] im = new double[len];
for (int i = 0; i < len; i++) {
re[i] = i;
im[i] = i;
}
for (int i = 0; i < len; i++) {
re[i] = re[i] + re[len -1 - i];
im[i] = im[i] + im[len -1 - i];
}

C# and Java: 100-150ms
I think structs can increase perfomance!

mthornton
Offline
Joined: 2003-06-10
Points: 0

Many C++ implementations will also pad a structure to a multiple of 8 bytes (e.g. 16). Then if you are allocating them separately on the heap the default new/malloc will add further header words (typically another 2), so we are now up to 24 bytes which is the same as Java.
It is only when you want a large array of these objects that the difference is significant. Then you do get down to 16 bytes per element vs 28 for Java (including a word for the reference).
Someone else suggested flattening these cases to something like:
float[] x;
float[] y;
float[] z;

While this achieves the efficiency the cost in maintenance is significant. Instead of just writing Complex.add(Complex a), we also have ComplexVector.add(Complex a), ComplexMatrix.add(Complex a), and many more combinations that require individual implementation (and extra opportunities for error).
However, if a lightweight class system was implemented, it would also be very desireable to allow generics to be used with them (which would require code expansion rather than erasure).

peterkessler
Offline
Joined: 2004-10-27
Points: 0

> new's can be particularly expensive (not so much
> on single processor machines, but definitely on
> multi-processor machines).

What makes you think that allocation on multi-processors
is more expensive? The HotSpot virtual machine has
thread-local allocation regions so there's no locking
required for allocations (except the ones that refill
the thread-local regions, and we have heuristics to
make that rare).

walter_bruce
Offline
Joined: 2004-11-19
Points: 0

Both allocation and synchronization were more expensive on multiprocessors at least under 1.4.2. See the discussion at:

http://www.javagaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=Tuning;acti...

If you scroll down to my third post on that thread, you will see some benchmark numbers for single vs. dual processors.
I haven't rechecked under 1.5, so this may have changed in 1.5.

But saving some allocation is only a minor side-benefit of extended primitives. The real benefit is the ability to directly embed them in larger objects without the memory overhead of the extra object headers and references, and without the performance overhead of the extra indirection.

Lacking the standard object header, which contains the class pointer and data for the object's lock, an extended primitive's type must be statically known at compile time, they cannot have virtual methods, and you cannot call wait() on one (i.e. they behave more like a primitive than like an subclass of Object). But the memory and performance savings would be well worth it for many things that would otherwise have to be represented as small objects.

beders
Offline
Joined: 2003-07-24
Points: 0

I like the idea of having some sort of downgraded struct.

If you consider ints, longs etc. as a chunk of bytes (4,8 etc.),
why not allow primitive types that are larger chunks of bytes.

It boils down to having a byte[] with structured access (a bit like gather/scatter from NIO)

Nice idea, reall!

mayhem
Offline
Joined: 2003-06-11
Points: 0

I don't understand why you need them. Ok, you save a few bytes here and there, but nothing big. You can easily create an array class for Vector3f:

class Vector3fArray {
private float[] x;
private float[] y;
private float[] z;
...
}

So, I don't buy the memory argument.

As for performance I'm not sure there would even be a performance increase. Do you have any benchmarks that backs up your claims?

kersam
Offline
Joined: 2004-11-18
Points: 0

Sorry, but

class Vector3fArray {
private float[] x;
private float[] y;
private float[] z;
...
}

Is very ugly and not OO.
Consider

Vector3f[] v = new Vector3f[1000];

The array would consume 1000 * 12 ~ 12K + array_overhead
if Vector3f is a extended primitive.

If Vector is a class
Vector3f[] v = new Vector3f[1000];
for (int i = 0; i < 1000; i++) v[i] = new Vector3f();

would consume 4 * 1000 + 24 * 10000 ~ 28K!!!

Another point:
If you design a class, you have to decide if you make it mutable or immutable.
If the class is mutable, you have good performance (maybe), but higher risk of errors. If you make it immutable, you have to create a new object if you do a change.

dog
Offline
Joined: 2003-08-22
Points: 0

> Sorry, but
>
> class Vector3fArray {
> private float[] x;
> private float[] y;
> private float[] z;
> ...
> }
>
> Is very ugly and not OO.

Why?

You go on to show us an example with Vector3f[] why is an array of vectors more OO than a specialized Vector3f object?

The proposed implementation Vector3fArray with its private arrays has the advantage of allowing varios implementations. For example you could use sparse arrays or some compacting technique to store your arrays if they were large. Whereas Vector3f[], even if a primitive would not have this advantage.

A good library could solve the Vector problem. In fact you could use the envelope/letter idiom (See Coplien's Advanced C++) to implement this. How?

You write cool classes that give you all the operations you like with a reasonable interface. But between each other these vector classes represent the vectors as simple 3d arrays. So in fact, your Vector3fArray stores an array of arrays of size 3, not the whole Vector3f object.

Isn't this similar to the concept of a flyweight?

I believe such a technique could give you even better space characteristic (maybe even performance) than a boxed up extprim feature. ::)

mthornton
Offline
Joined: 2003-06-10
Points: 0

With Complex[] you can multiply every element by some value using code like:

Complex[] v;
for (int i=0; i v[i] = v[i].multiply(z);

Now what happens if you have a ComplexVector? You either re-implement multiply to achieve efficiency or you extract a Complex value and then replace it with the result which will be slower.

So why can't we have the simplicity of a single implementation of multiply without losing performance?

spiff
Offline
Joined: 2003-06-10
Points: 0

This could almost be done entirely by the compiler, without changes to the VM. The compiler could just "inline" extended primitives into their constituent parts (e.g. if you have a "vector3f foo" field, the compiler could turn it into "float foo_x, foo_y, foo_z". I think there are only two problems with this approach. One is overloaded methods (i.e. if you have two methods foo(vector2f) and foo(complex2f), they would be turned into foo(float x, float y) and foo(float real, float imaginary), causing a conflict). This could be fixed by simply disallowing overloading methods such that this happens. The more serious problem arises from returning extended primitives. Since only one value can be returned, you'd have to encapsulate the extended primitive in a temporary object. Unless the VM can special-case these type of returns, you might have serious performance implications. For this reason, I think adding a "return multiple values" feature that is actually handled at the VM level would be a good idea.

trinition
Offline
Joined: 2003-07-29
Points: 0

This sounds a lot like stack-based objects. Why not just allow some new sort of object construction that places the object on the stack? If it has finalizers, the allocator would set a flag in the stack indicating that finalizes must be run before the stack exited:

Foo foo1 = new Foo("Heap");
Foo foo2 = new.local Foo("Stack");

abies
Offline
Joined: 2003-06-10
Points: 0

It is different from stack allocated objects - you can put extprim structure inside heap allocated object. You can probably also make array of such structures.

trinition
Offline
Joined: 2003-07-29
Points: 0

> It is different from stack allocated objects - you
> can put extprim structure inside heap allocated
> object. You can probably also make array of such
> structures.

But that sounds like an arbitrary constraint. Some memory constraint? Who said objects have to be large? Small objects can fit on the stack equally well.

Suppose I had a Vector class I used all over and it had three float members. Now, to put it on the stack in your world, I've have to extract it to an 'extprim' because 1st-class objects can't be on the stack. So do I write adapter methods to go to/from the extprim represenation? Do I duplicate all of the logic in the methods of Vector?

I just don't follow your reasoning to so narrowly defined what can be on the stack.

abies
Offline
Joined: 2003-06-10
Points: 0

Original idea here is to allow light-weight structures, to allow embedding substructures inside objects without using separate objects. Please forget about stack and reread original suggestion again. Basic points are passing by value, embedding directly into objects without being separate object, having 1000 element array of vector3f to be 1 objects instead of 1001 objects. Allocating on stack is just a one small feature which can be done by the way for free (as extprims would be probably mapped to number of primitive fields, so they would just fit in number of local variables on stack).

abies
Offline
Joined: 2003-06-10
Points: 0

What about methods for ext primitives ? As I understand the current proposal, they would not be able to modify variables of 'structure' ? In following example

extprim vector3f {
float x;
float y;
float z;

void localNormalize() {
float d = (float)Math.sqrt(x*x + y*y + z*z);
x /= d;
y /= d;
z /= d;
}

vector3f normalize() {
float d = (float)Math.sqrt(x*x + y*y + z*z);
return vector3f(x/d,y/d,z/d);
}

}

Would it be possible to have method localNormalize ? If yes, how it is supposed to work without pointer to 'this' - structure it is supposed to work on ?

What about arrays of ext primitives ? Mapping of ext primitives to java.nio.ByteBuffer (to allow communicating with native structures, like opengl/network data) ?