-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-12030] Fix Platform.copyMemory to handle overlapping regions. #10068
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package org.apache.spark.unsafe; | ||
|
|
||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
|
|
||
| public class PlatformUtilSuite { | ||
|
|
||
| @Test | ||
| public void overlappingCopyMemory() { | ||
| byte[] data = new byte[3 * 1024 * 1024]; | ||
| int size = 2 * 1024 * 1024; | ||
| for (int i = 0; i < data.length; ++i) { | ||
| data[i] = (byte)i; | ||
| } | ||
|
|
||
| Platform.copyMemory(data, Platform.BYTE_ARRAY_OFFSET, data, Platform.BYTE_ARRAY_OFFSET, size); | ||
| for (int i = 0; i < data.length; ++i) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yes, this is a good test. I missed this earlier. Nits: casts usually have a space afterwards in Java, and
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Old habits die hard :) I feel pretty good that if this test case passes then unsafe.copyMemory works. There is a question of whether this behavior is unspecified and can between different jvms. |
||
| Assert.assertEquals((byte)i, data[i]); | ||
| } | ||
|
|
||
| Platform.copyMemory( | ||
| data, | ||
| Platform.BYTE_ARRAY_OFFSET + 1, | ||
| data, | ||
| Platform.BYTE_ARRAY_OFFSET, | ||
| size); | ||
| for (int i = 0; i < size; ++i) { | ||
| Assert.assertEquals((byte)(i + 1), data[i]); | ||
| } | ||
|
|
||
| for (int i = 0; i < data.length; ++i) { | ||
| data[i] = (byte)i; | ||
| } | ||
| Platform.copyMemory( | ||
| data, | ||
| Platform.BYTE_ARRAY_OFFSET, | ||
| data, | ||
| Platform.BYTE_ARRAY_OFFSET + 1, | ||
| size); | ||
| for (int i = 0; i < size; ++i) { | ||
| Assert.assertEquals((byte)i, data[i + 1]); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some benchmarks, there is no performance difference between chunked copy than single
_UNSAFE.copyMemory, agree with @srowen , single call to_UNSAFE.copyMemoryshould be enough.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was getting at something different -- does
_UNSAFE.copyMemorywork reliably with overlapping memory regions? I hope so, I imagine so. But this method's correctness still depends on it, so it's probably worth unit-testing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davies I don't think it was chunked this way for perf. Looking at the comment for UNSAFE_COPY_THRESHOLD, it seems to be for some other purpose. I'm not familiar with this enough to judge if we need that.
@srowen I'm looking to see if I can find docs on what unsafe does but haven't found anything definitive yet. The test case you originally suggested is very similar to what I have no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm i don't think the copy block by block is done to improve performance. I believe it was done to allow safe points when copying a very large region.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still have a question. Both
dstOffsetandsrcOffsetare offsets. They are not absolute addresses. Based on the values ofdstOffsetandsrcOffset, we are unable to know if source is behind target, right?Based on the code changes, I do not understand how it works. Could anybody give me a hint? Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davies Thank you for your explanation! The code changes look confusing for the C/C++ people, I think. Do you think we should add the comments for clarifying your point for better readability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davies
copyMemoryis a general function. How can we know they are from the same object or not? Like in the following functionputNewKey, the function caller has to guarantee they do not break our hidden assumptions when calling the functioncopyMemory, right?I am just afraid the other coders might not realize we have such an assumption in this low-level general function
copyMemory. Let me know if my concern is valid. Thanks!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the "hidden assumptions"? The
srcanddstcan be same object or not, the offset can overlap or not, we don't have a limitation forcopyMemoryright?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In JVM, two objects (or memory block) can't overlap each other (assuming the two offsets are in valid range). The only case that src and dst could overlap is they are exactly the same value (pointer), so we can only compare the offsets.
After this patch, we can use copyMemory() without worrying about the src and dst are overlapped or not, it always works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uh, I see. Thank you very much!