You may be interested in the macros @code_llvm and @code_native which show the bytecode and assembly[1] instructions of an expression, they may be helpful tools for exploring Julia’s low level memory semantics.
For example, here is @code_native testIt(v1, v2) using your definition above (with some verbose output removed):
The llvm bytecode from @code_llvm is more concise, but this shows the actual mov instructions being invoked which is what I assume you’re asking about.
Cf godbolt compiler explorer. Read what your compiler emits for your examples, and pray that you don’t need to understand pointer provenance (shudder). Your function does
No I cannot do this, because obviously there is additional context here. The entire contents of the program I am writing is not these 4 lines in the REPL.
This is why I was skeptical about providing an exact example from another language.
I am not asking, can I do a literal line-for-line translation from this C++ code to Julia, I am asking in an abstract sense does Julia have efficient move semantics. The C++ code is just one of many possible examples which show what efficient move looks like in that particular language.
What? No. The machine instruction mov is totally irrelevant to (for example) std::move, which is a language level construct.
I would guess many of these function calls are considered to be private implementation details too?
Then you should specify what move semantics should look like in julia. Your examples seems to suggest that you want to do reassignment of variables by calling a function, I’m not sure.
Do you have an example of some julia code where “efficient move semantics” would come in handy?
I mean, Julia can do copy elision in general if that’s what you’re referring to. It’s just not guaranteed to happen Depending on the complexity of your example as well as the surrounding context, it may or may not happen. In the examples you’ve given so far, it is extremely unlikely to happen, since the original object is still referred to after the function call and/or escapes to global scope.
I don’t know if that’s what you see as “efficient move semantics”, because the whole concept of a moving an object from the memory of one variable to the memory of another variable doesn’t really make sense in Julia, because the underlying memory doesn’t belong to the variable. The variable is just a label for the object, which ultimately owns the backing memory (though that is not generally exposed). That’s also why I gave the example with a* above. In that example, not even “copy elision” would take place, because semantically no copy is made in the first place…
julia> [1].ref = [1].ref
ERROR: setfield! fields of Array should not be changed
That being said, you can do that and feel only slightly dirty – you’re misusing an API, not a core language invariant.
Then the answer is “yes”, efficient moves exist. And they mostly work the same as in C (you need to forget C++, that’s a very bad starting point for thinking about julia). The main constraint compared to C is that you cannot take the address of local variables. But if your local variable is itself an addressable (i.e. mutable) object, then it is its address. So this extra level of indirection must be explicitly included in your code.
Julia has no concept of “lvalues”. Assignments like a = .... are not opererations, then simply assign a name to a value. Operations like x[] = ... or x.foo = ... are instead function calls that mutate x. Literally:
julia> baz(x) = x.foo="a"
baz (generic function with 1 method)
julia> @code_lowered baz(1)
CodeInfo(
1 ─ Base.setproperty!(x, :foo, "a")
└── return "a"
)
So the way do do “move semantics” in your sense is that you have an object container that first points (refers) to some value. And then you mutate the container to point to another value! Then everyone who has the identical container in hand will see this change in value.
So you don’t want to copy a big value? Well, make sure that the container contains a reference, not a bunch of data. Like void** instead of void* in the C example. “Efficient move” means changing a pointer instead of doing a big fat memcpy.
Local variables are not containers. They have no address. Immutable values have no address either,
julia> Base.pointer_from_objref(5)
ERROR: pointer_from_objref cannot be used on immutable objects
PS. With “reference” I mean “GC-managed pointer”, in the context of julia, which afaiu has nothing to do with C++ references, but is about the same as in e.g. java. Both “pointers”, in the sense of “Ptr”, and “references” exist, and both have the same in-memory representation. But “references” are conflated with the object they point to, on a language level; and since they must be tracked by the garbage collector, there are write-barriers and a shadow-stack involved; dealing with references in C / julia.h is somewhat of a pain, though not as bad as JNI.
This conversation appears to be running around in circles where a large group of people are repeating the same information to one person who is either unconvinced by those arguments, or not understanding what is being said.
Either way, I think continuing this is probably not a very productive use of people’s time and will likely just lead to sore feelings.
I’d urge people to take a step back and let things slow down, and maybe let one or two people continue discussion at a time. Historically, having one person that feels like they have to respond to a large group of other people all giving similar but different arguments to them about how they are wrong has not ended well.
I think the issue here is no one wanted to outright say that the answer to my question is “no Julia does not have efficient move semantics”.
I see this often, across all different languages. Some people start to get defensive when someone asks a question which reveals a shortcoming in their favorite language compared with some other language.
Rather than simply say “no it can’t do that”, such people try to discredit the author/imply that they are stupid/imply they don’t understand what they’re talking about/pick alternative way to distract from the original question.
From my point of view, I don’t have a favorite language, so I don’t really care if someone points out that language X does something which language Y doesn’t do. While I’m interested in languages generally, what I’m interested in is the differences between them.
Just because I gave an example written in C++ doesn’t mean I’m going to abandon Julia and go back to C++. C++ has plenty of other problems which would push me in a number of other directions first. Even if I did so, of what consequence would it be to anyone else?
My question was quite simple, I don’t think it needed nearly 50 comments to arrive at this conclusion. I can understand why someone may not have understood from my initial post what I was asking about, but I’m fairly confident it was unnecessary to still fail to understand the point after multiple clarifications.
Even if someone has only used languages similar to Julia or Python previously, it isn’t that hard to understand that swapping two references to two pieces of data results in a minimal amount of instructions executed and therefore highly efficient move semantics.
The bare minimum I would have expected would be for someone to realize what I am asking about isn’t a concept which exists in the Julia world. In which case, there is no point contributing to the thread, because there is no point contributing to a thread where you don’t understand the fundamental concept which the entire thread depends on.