i

Started by
84 comments, last by GameDev.net 22 years, 4 months ago
Unified Object hierarchy refers to the inheritance mechanism used by Java. Every class in Java is inherited from class Object (either explicitly or implicitly). This isn''t the case in C++.

Now, whether it is a good thing, is open to debate.... The only time I''ve found a unified object hierarchy useful is when I''m writing containers. This isn''t necessary in C++ as it supports templates
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
Advertisement
Beleive you me JAVA is extremley fast. Go download the new JRE 1.3.1_01 and the new JAVA 3D api and see how fast it runs! Also downlaod a few of the examples to see...

Also I have done work with XML and I have been able to work with 100MB XML documents no problems.

It''s like supper fast you know, like totally cool, like you know!

Garbage collection since it has been brought up... Here is how JAVA works... The garbage collector will only clean up variables that have no reference to them. Bassically you set the variable to NULL and eventually the garbage collector will pick it up. Also the garbage collector will clean up variables that have fallen out of scope. The garbage collector can also be called using System.gc() (something to that extent), BUT this does not mean the garbage collector will clean up. When System.gc() is called this is merley a sujestion to the JRE to invoke the garbage collector. It doesnt mean it will...

Again I will keep saying this for JAVA really depends on how good the JRE is. It will be the same case for C# and any byte code or dynamically compiled languages or what ever the case. Also when running JAVA you can specifi stack sizes and other paramaters, for performance...
For those intersted. This is how JAVA and C++ can work together using the JNI... I found this on the IBM site and is a bit dated, but demonstrate the point.



    // Standard main JAVA class.public class MyApp1{  public static void main( String[] args )  {    System.out.println( "Hello, world!" );  }}        // C code to use the JAVA class#include <windows.h>#include <stdio.h>#include <jni.h>#define MAIN_CLASS  "MyApp1"int main( int argc, char** argv ){  JNIEnv*        env;  JavaVM*        jvm;  JDK1_1InitArgs vmargs;  jint           rc;  jclass         cls;  jmethodID      mainId;  // get CLASSPATH environment variable setting  char* szClasspath = getenv( "CLASSPATH" );  vmargs.version = 0x00010001;  //version 1.1  JNI_GetDefaultJavaVMInitArgs( &vmargs );  //init vmargs   //the classpath returned by JNI_GetDefaultJavaVMInitArgs is wrong  vmargs.classpath = szClasspath;  rc = JNI_CreateJavaVM( &jvm, &env, &vmargs );  // create JVM  if( rc < 0 )  {    printf("Can't create Java VM\n");    return 1;  }  //load the class containing the static main() method  cls = (*env)->FindClass( env, MAIN_CLASS );  if( cls == 0 )  {    printf( "Could not find class %s\n", MAIN_CLASS );    return 1;  }  // find the main() method  mainId = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");  if( mainId == 0 )  {    printf( "Could not find main()\n" );    return 1; /* error */  }  (*env)->CallStaticVoidMethod(env, cls, mainId, 0); // call main()  (*jvm)->DestroyJavaVM( jvm );  // kill JVM  return 0;}  


JNI is used when you really need a specific OS functionality that is not part of JAVA example, using the mouse wheel, graphics, joystics etc...

Edited by - ANSI2000 on December 19, 2001 10:39:28 AM
The performance still isn't too great under Linux. But it isn't apalling either.

Oh and for anyone who is interested, here are some benchmarks that compare Java and C++.

http://www.javaworld.com/javaworld/jw-02-1998/jw-02-jperf.html

And a very good article here (hey Magmai, there's an FFT benchmark too )
http://www.aceshardware.com/read.jsp?id=153

You need to take into account that the compilers and VMs used aren't cutting edge. A lot has probably changed by now with the introduction of HotSpot. But then, I'm just speculating here.


Edited by - NuffSaid on December 19, 2001 1:36:34 PM
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
quote:-TheDragon
ASI2000 posted this:
> Also I mentioned that the JAVA3D API uses both OGL and DX. No it is not written in JAVA. JAVA uses the JNI > (Java Native Interface) to access the OGL and DX DLLs. Yes folks you can mix and match JAVA with C++. You > can implement lets say all your game logic in JAVA and all your graphics code in C/C++. Then you use a set > of libraries to be able to call JAVA code within your C/C++ code.

Very smart IMO, but someone mentioned that the one function call to the C++ or assembler code would kill you anyway, since you were trying to boost speed by using a faster language. I agree, if you had to call that function say everytime you wanted to draw the vertice, and were keeping track of it in java, then that would be a killer performance hit. But generally, I think its a great idea to control your program flow with Java (if you use it, anyway) and say for DX/OGL C++ specific stuff (like direct sound) you would then proceed to call something from another language. I'm definatley going to experiment with that logic and see where I can get; might code a nice emulator in java ;-)

I agree completely, at the high-level scripting of the game it's very desirable to have a COM/.Net interface exposed so that you could script the game using VB/VBA/Java/C++/Python/Whatever-rocks-your-world, so that you don't have to spend time writing your own compiler (QuakeC/UnRealScript). The physics engine and collision detection/spartial sorting algorithms are probably best left in C++.



quote:
>> Languages are more like toolboxes than single tools (and the Java toolbox fits inside the C++ toolbox). >>

>> No design exist that can be better implemented nor expressed in Java because Java has a sub-set of C++'s design capibilities - the best it could be is equal. >>

Both of these arguments are based on the same idea as the one that you can code a Java runtime environment in C++, but not the other way around. In other words, they're only true in theory but doesn't say anything about real-world application development where you don't have 2 years to spend on coding what Java gives you for free in C++. I would claim that the argument is about as relevant as that for using assembly macros over C/C++ for the same reasons.

If you decide you need to use an assembly macro, every C/C++ compiler will let you. And C++ has an large number of extensive libraries to choose from if you're willing to accept the capibilities of OPC (OtherPeoplesCode). The classic problem we have is that libraries will do about 80% of what we need, so you either give-up that 20% or do it yourself. Does Java supply an asyncronous event driven socket implementation utilizing a kernel mode thread pool (and perhaps scatter/gather IO)? Amazingly enough very few libraries do, but this is what is needed for a high-performance Internet server. If all you need are blocking sockets, those are a dime a dozen - every library seems to have one of those.


quote:
It's also not true that C++ has all Java's design capabilities since it lacks a unified object hierarchy...

Professional C++ platforms come with an object heirarchy, e.g. VC comes with MFC, and BCB comes with the VCL. I'll give you that they vary between packages.

quote:
...and has to use bloated template solutions instead.

I simply do not understand this. The STL sort is the fastest sort not written in assembly. If you are concerned about file size, then let's have a look:

Using MSVC6 and it's STL implementation, I get an exe size of:
48k without any STL.
52k with a vector
52k with a vector and a map.
52k with two vectors of different types and a map.
56k with two vectors of different types and two maps of different types.
56k with three vectors of different sized types (an int, a double, and a struct) and two maps.

Maps, which are one of the more complicated containers, appear to bloat your code by about 1.5k _per type of map_. An instance of a map consumes 16bytes when empty (as do vectors). So while I agree that 8k isn't entirely insignificant, the increase is so small that it's hard to measure accurately without doing alot more investigation.

; 64   : 	std::for_each(vWierd.begin(), vWierd.end(), WierdAssign() );  000e2	8b 4d c4	 mov	 ecx, DWORD PTR _vWierd$[ebp+8]  000e5	8b 45 c0	 mov	 eax, DWORD PTR _vWierd$[ebp+4]  000e8	c6 45 fc 03	 mov	 BYTE PTR __$EHRec$[ebp+8], 3  000ec	eb 16		 jmp	 SHORT $L11551$L11462:  000ee	c7 00 01 00 00	00		 mov	 DWORD PTR [eax], 1  000f4	c7 40 04 02 00	00 00		 mov	 DWORD PTR [eax+4], 2  000fb	c7 40 08 c3 f5	48 40		 mov	 DWORD PTR [eax+8], 1078523331 ; 4048f5c3H  00102	03 c6		 add	 eax, esi$L11551:  00104	3b c1		 cmp	 eax, ecx  00106	75 e6		 jne	 SHORT $L11462     

There's the assembly for an inlined vector iteration using a functor to do a simple assignment. It expands to 10 op codes... and consumes 36bytes.


Here's the same thing using a function call:
; 65   : ; 66   : 	std::for_each(vWierd.begin(), vWierd.end(), std::mem_fun_ref(Wierd::Assign) );  00108	8b 55 c4	 mov	 edx, DWORD PTR _vWierd$[ebp+8]  0010b	8b 4d c0	 mov	 ecx, DWORD PTR _vWierd$[ebp+4]  0010e	eb 07		 jmp	 SHORT $L11552$L11491:  00110	e8 00 00 00 00	 call	 ?Assign@Wierd@@QAEHXZ	; Wierd::Assign  00115	03 ce		 add	 ecx, esi$L11552:  00117	3b ca		 cmp	 ecx, edx  00119	75 f5		 jne	 SHORT $L11491$L9636://...//...//...?Assign@Wierd@@QAEHXZ PROC NEAR				; Wierd::Assign, COMDAT; _this$ = ecx; 20   : 	x = 1;  00000	c7 01 01 00 00	00		 mov	 DWORD PTR [ecx], 1; 21   : 	l = 2;  00006	c7 41 04 02 00	00 00		 mov	 DWORD PTR [ecx+4], 2; 22   : 	f = 3.14f;  0000d	c7 41 08 c3 f5	48 40		 mov	 DWORD PTR [ecx+8], 1078523331 ; 4048f5c3H; 23   : 	return(0);  00014	33 c0		 xor	 eax, eax     

That takes up 7 op codes for the function call and 4 op codes for the functions... it used 17+20=37bytes. It's actually 1 byte larger than the non-lined verion and slower! You would save 20bytes for subsequent iterations, but that's a small price to pay for premium performance.
I imagine that the Java containers could be equally optimized, but I somehow doubt it. This is the wrong place for run-time polymorphism (which are more expensive than the function calls above).


I also think that designs that doesn't use operator overloading are better expressed in languages that do not support operator overloading since it means that programmers can tell right away what the operations do, and don't have to have to check all the invovled objects and namespaces for overloaded operators (and failure to perform such checks is another source for bugs).

Only if the overloader defines unconvetional behavior - would you need to look-up what + does for a class named CComplex? or * for CQuanternion? If you don't like operator overloading, you don't have to use it - but you can if you want to. That kind of flexiblity is one of the strengths of C++. Removing a feature such as this means you lose the benefits of good code while never encountering the problems of bad code, again that's the trade-off between the philosophical difference between Java and C++.
If you don't use an operator in an expected way it generally doesn't compile. This is another strength of C++, it detects many mistakes at compile time that other languages defer to run-time (due to misplaced run-time polymorphism).


quote:
Still, I do agree that C++ might very well be better when development time and bug frequency aren't factors in the equation, or when you are performing pure number crunching operations. Those requirements have never applied to any of my work though. Oh well, now I'll try to be out of here

The vast majority of the bugs I've had to track down and fix that took more than about 5minutes were a logic errors - which are present in all programs, regardless of the safety nets they provide. In two years we had one memory stompage problem that took about 6hours to track down. I'm not willing to give up pointers for about 6hours of aggregate time a year - YMMV (You Mileage May Vary). Most of our bugs are race-conditions or out-of-order operations. Most of our maintaince is performed to improve the UI (which is generally the source of the out-of-order bugs). Most of the race conditions are from a time when the team was first learning abuut multi-threading, and the out-of-order bugs are because it's a legacy project with lots of non-encapuslated code (never mind OO).



...
quote: null_pointer
By "functors" you mean functions as template parameters?

Sort of - a functor is technically a class created for the purpose of overloading operator(), an instance of which (often temporary) is passed as a parameter to a template function (as one of the templatived parameters.
Here's the WierdAssign functor I used in the disassembly above.
          struct WierdAssign	{	operator()(Wierd& w)		{		w.x = 1;		w.l = 2;		w.f = 3.14f;		}	};//Used herefor_each(vWierd.begin(), vWierd.end(), WierdAssign() );          

The syntax is quite klunky, a-mon-avis, but it lets you completely inline code, maintain encapsulation, AND use generic (compile time polymorphism) methods (i.e. The STL).
Functors are what make the STL so fast - the C sort uses a call back, which often takes more time than the comparison operation does. An optimized search/sort routine can be quite small - a few dozen bytes or less, so 'code bloat' just doesn't apply, when you can get a 25%-600% performance increase for it ("Effective STL", Scott Meyers).

And I know my post are long, but I had to take notes reading yours


quote:
And a very good article here (hey Magmai, there's an FFT benchmark too )

I've looked it over extensively before, and adjust my opinions of Java accordingly. I no longer claim that Java is slower than my dead Grandmother (God rest her soul). This is a good point that the 'speed' of Java code is compariable to C++ code. And is _the reason why you still need assembly. Notice they disqualify any code that uses assembly? It's twice to ten times faster than the algorithms listed there. Without FFT/DCT algorithms written in optimized assembly, OTF MPEG2 decoding wouldn't be feasible.



I'm trying to get an assembly dump of the templatized swap function at work, but I'm having difficulty because the compiler keeps eliminating the swap entirely on me.

Is it possible to have a look and see what the Java code actually does (the disassembly)?

Here we go:
  template<typename T>void swap(T& a, T& b)	{	T t(a);	a=b;	b=t;	}  

; 49   : 	swap(i1, i2);  0000d	8b 30		 mov	 esi, DWORD PTR [eax]  0000f	8b 11		 mov	 edx, DWORD PTR [ecx]  00011	89 31		 mov	 DWORD PTR [ecx], esi  00013	89 10		 mov	 DWORD PTR [eax], edx  

4 op codes.
I had to make a function and swap a bunch around and do some math on them and return the result to convince the compiler that it really needed to perform the swap.
Magmai Kai Holmlor

"Oh, like you've never written buggy code" - Lee

"What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO

Edited by - Magmai Kai Holmlor on December 20, 2001 2:04:50 AM


Edited by - Magmai Kai Holmlor on December 20, 2001 2:12:28 AM
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
I am officially out of here but you who have been talking about pushing single vertices from Java to native code should take a look at the java.nio packages in jdk1.4 that allow you to share memory buffers between the two layers, allowing instant transfers of vertex arrays and other data. It''s one of the features that make Java a serious alternative for modern games.

Btw, with those packages you also get support for non-blocking TCP sockets, I believe Magmai was asking about that. I haven''t looked at the details of the implementation, but Sun does know a bit about server technology.

Henry

This topic is closed to new replies.

Advertisement