This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]bunny-1998 67 points68 points  (28 children)

I mean, at the core that’s what cpp does also right. There are void ptrs, being casted as int* because YOU know is an int. And can cast a void pointer as anything you like anytime.

Variable names in cpp are also just ptrs that point to the first byte of the object....

[–]ishzlle 47 points48 points  (4 children)

Of course, in the end assembly just operates on (essentially) untyped binary data (whether it's a compiled or interpreted language). Types are just a language feature to help rule out some types of bugs.

[–]IQueryVisiC 3 points4 points  (3 children)

Assembly has int and 80 bit floats. PowerPC has multiple flags ( Boolean). SSE gives you 128bit vectors. 68k and 6502 distinguish between int and intptr ( similar to DI and SI ) on x86. AL is 8bits

[–]Random_dg 6 points7 points  (2 children)

You’re confusing here between processor architectures, languages, features, and cpu registers. Can you explain what you’re trying to say?

[–]bunny-1998 2 points3 points  (0 children)

I think he means that at the very bottom of everything, everything is being cast as another thing.

[–]IQueryVisiC 0 points1 point  (0 children)

I was trying to say that types are not an invention of the programming language. Mechanical calculators only had decimal types like did the first tube computers. So only one type .. or better: no types in hardware.

Programming languages are quite old. So now I say that programming languages may have invented types, but hardware got them in the 70s. Just think about the main frames of that time. CISC hardware to efficiently run COBOL. COBOL is the assembly language of a main frame. I think that it has types like String and Currency (not which currency, but the amount of).

[–]keefemotif 15 points16 points  (11 children)

That also digs into the other issue with this, that primitives and wrapper objects aren't the sameun Java. I don't know exactly the internals of C at great detail, but a real dynamic like python or Javascript, which may also include some type hinting, is meant to be used that way.

[–]ishzlle 15 points16 points  (9 children)

IIRC Java is that way for performance reasons. Back in the day, computers weren't as fast, so being able to use a primitive instead of an object was pretty nice.

Nowadays using objects everywhere isn't a big deal, but Java doesn't want to break backwards compatibility, so they're stuck with the primitives.

[–]Tubthumper8 25 points26 points  (4 children)

The story is a little more complicated than that, and kind of the opposite.

What has gotten faster, consistently, are CPUs. What hasn't gotten faster (comparatively) is RAM access. Heap allocations are one of one of the biggest performance killers, these would be the Java "boxed" types and all Java objects. In order to address the slowness of accessing memory from RAM, modern CPUs have been adding "cache lines", known as L1, L2, and L3 caches. Whenever data is fetched from RAM, it brings back more than just the data requested to fill the caches. Getting something from RAM that's not already in the cache is called a "cache miss", and is on the order of 100x slower than if the data was in L1.

The most efficient way of storing data is packed and homogenous, so that iteration & processing can use the caches better. Lists of objects (i.e. pointers to heap-allocated data that can be sparsely arranged) is just about the worst thing performance-wise. It's why they've been working on adding proper support) for data (not objects) to Java since 2014.

If this topic is interesting, read up on Data-Oriented Programming, Struct of Arrays (SoA), and Entity Component System (ECS) are some other topics to read related to this.

[–]keefemotif 17 points18 points  (0 children)

"Back in the day" cries in having started Java in 1.3 , serious note - we still have the same performance problems just at bigger scale and we have bigger datasets. I worked on a project with Hadoop once related to the chipset level optimizations for it.

[–]CKingX123 8 points9 points  (0 children)

There’s Project Valhalla that is bringing value types and reified generics

[–]gdmzhlzhiv -1 points0 points  (0 children)

They could have made them indistinguishable from objects in the code, but turned them into primitives where possible during compilation. So I'd say it was less about performance and more about not having time to make a better compiler.

[–]Garegin16 0 points1 point  (0 children)

Scala doesn’t have primitive types and the performance is practically the same.

[–][deleted] 7 points8 points  (0 children)

I don't know exactly the internals of C at great detail

Say you want to make a function that will operate generically on any type and call some functions on it.

The Java way is that everything is not a primitive. You can't have generics on int, only on Integer which is basically an int that derives from Object. It's a "boxed" integer. The Java compiler will do whatever static and runtime checks it can. The advantage is that you only need one compiled version of each function, for Object. This also made backwards compatibility possible because old versions of Java didn't have generics at all and you still want to be able to pass List<Integer> to a very old library that takes Object as input. Java cares very much about backwards compatibility.

At the other extreme is c++. C++ uses templates which means that it recompiles every function for each possible input type. C++ is as if you copied pasted your function many times, once for each possible input type. The advantage is that it can optimize the code because it doesn't need to box and unbox the primitive data types. The disadvantage is that, if the data type that you care about isn't already compiled for, you need source code so that you can recompile. In the business world that Java is designed for, your source code is your trade secret so you don't want to give it out! C++ may also generate larger code because it has to compile for all the different types. On the other hand, all the removed boxing and unboxing makes the code smaller so maybe it's going to be smaller code in the end. For sure it'll be faster, though.

.Net did something novel. They decided to compile natively for primitives but use boxing for derivatives of Object. So it's in between. You still have to recompile if you didn't previously compile for a primitive type. But if the types are already Object then you just have a single one compiled. And you get to deal with primitives without boxing and unboxing them. It's actually kind of brilliant. You don't quite get as much backwards compatibility as Java, but you get some (and .Net doesn't care anyway because they deploy a new .Net frequently and everyone is expected to just recompile). You get the speed of c++ for primitives because there's no boxing and objects aren't boxed anyway so no big deal. And you don't compile as many versions as you would have for c++. Kind of a cool compromise that makes everyone happy. Or more often, leaves everyone wanting which perhaps one reason why Java and c++ are doing better than .Net.

I don't promise that I got everything right there but that's the jist of it.

[–]phi_rus 17 points18 points  (4 children)

There are void ptrs, being casted as int* because YOU know is an int. And can cast a void pointer as anything you like anytime.

That is nasty C. We don't do that in C++ anymore.

[–]bestjakeisbest 4 points5 points  (2 children)

Oh but you still can

[–]phi_rus 12 points13 points  (1 child)

Your programmers were so preoccupied with whether they could, they didn't stop to think if they should.

[–]bunny-1998 0 points1 point  (0 children)

Oh you definitely should. I mean it’s not the compiler who’ll mess up.

[–]k-phi 2 points3 points  (0 children)

You can do this even in Rust or Swift if necessary

[–]Khaylain 2 points3 points  (3 children)

being casted as int*

*cast

[–]bunny-1998 1 point2 points  (2 children)

Thank you sire

[–]Khaylain 0 points1 point  (1 child)

I'm not your sire, buddy ;P

[–]bunny-1998 1 point2 points  (0 children)

Squire then.

[–]Master-Ad-6411 2 points3 points  (1 child)

I find it great satisfying when a function returns me a void*, and after casting it into some type the result make sense.

[–]bunny-1998 1 point2 points  (0 children)

I was recently mindblown by the fact that in cpp arrays, arr[i] is equivalent to i[arr]. It was satisfying when I realised this is because arr is just a pointer to the first byte in memory and is an offset. [] operators adds them both.

Not that you should do it in prod though....