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
Get test_memoryview passing #625
Conversation
Store hashes of memoryview objects when computed once, add a check for buffer disposal in __enter__, update tolist() and tobytes() to handle offsets and strides, arrays, and throw errors when attempting comparison operators with a memoryview.
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.
- The weakref stuff is somewhat hard to deal with since it relies on the GC. Usually you can get it working by adding a
gc.collect()
. I'm typically fine with skipping these tests since the memory usually does get collected at some point. - Looking at the CPython source it seems like the tuple creation is done in the getter so this is probably fine. One thing we might consider is changing the
IBufferProtocol.SubOffsets
to the typePythonTuple
?
Src/IronPython/Runtime/MemoryView.cs
Outdated
@@ -390,6 +416,12 @@ public sealed class MemoryView : ICodeFormattable { | |||
throw PythonOps.NotImplementedError("multi-dimensional sub-views are not implemented"); | |||
} | |||
|
|||
// We expect to get a numeric value when setting a paricular | |||
// element, which we will handle by breaking up into bytes | |||
if (!(value is int)) { |
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.
May need to be careful here since the type could be a BigInteger
or a user defined type (class test(int): pass
)?
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.
Ah I see; maybe I should be using something like Converter.TryConvertToInt32()
.
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.
Or Int64 -- since the opcode can have 8 bytes. I need to add a test for this.
|
Yes, you can just add something like |
suboffsets a PythonTuple.
It still consistently failed when run from make.ps1. The test currrently calls Currently, I just have the test skipping for ironpython implementations. |
It looks like changing the IsolationLevel seems to make
|
memoryviews. Set IsolationLevel=PROCESS for test_memoryview.
Just a quick update since I left this hanging so long. Since test_file seems to be a case of a weakref not releasing as well, I think they should be in the same status so I think I'll mark set IsolationLevel=Process as above. The remaining hangup that I'm working on is that my conversion to an int (line 421) doesn't work above for floats and the other types, and I'm trying to come up with a clean way of giving the correct responsibility of error handing to MemoryView and TypecodeOps. |
Sounds like you might need a switch based on format to do the proper conversions? |
I guess I think it should be done in MemoryView. This is a little closer, and it includes the change to _itemsize. Tomorrow I'll look into the set of typecodes that seem to be obsolete and other cleanup, and write some tests that are needed like the one for setting a double. |
Src/IronPython/Runtime/MemoryView.cs
Outdated
// TODO: BigInteger should also be merged into an integer once the int/long merge occurs | ||
if (result is BigInteger || result is double || result is float || result is Bytes) { | ||
// TODO: BigInteger/long/ulong should also be merged into an integer once the int/long merge occurs | ||
if (result is BigInteger || result is double || result is float || result is Bytes) { |
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.
float
probably needs to be cast to double
.
I wonder if there should be a .NET primitive to Python type helper somewhere? Maybe there already is...
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 believe GetPythonType
works for primitive types. I'm not sure if that's the functionality you're referring to.
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 meant something that essentially does what you do here: byte
/short
->int
, float
->double
, long
->BigInteger
, and so on for all C# primitive types. GetPythonType
does not do this since it will return you the Python version of the C# types (e.g. it will give you System.Int16
for shorts).
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 wasn't able to find anything that does this, so I added one such method in PythonOps
. I'm not entirely sure what the correct location for this would be.
Seems like a serious case of me not cleaning up the things I started but didn't like. Sorry for making you review all of this! |
I'm fine with reviewing, helps me get more familiar with the codebase. :) |
doubles are not rounded in for memoryviews with the 'd' opcode. Clean up unused code.
Src/IronPython/Runtime/MemoryView.cs
Outdated
/// for this MemoryView. | ||
/// </summary> | ||
/// <param name="value">The value to be set.</param> | ||
private void checkNumeric(BigInteger value) { |
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.
checkForOverflow
. Should this be in TypecodeOps?
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.
q
and Q
seem to be flipped here.
Edit: they were not.
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.
Probably.
Might be lost in the noise, but it seems like we're doing a lot of converting back and forth: object -> BigInteger -> object -> primitive type -> byte[] -> primitive type -> object (and then SetItem does a bunch of the same work). Anyway, I guess we should get it working first before worrying about optimizations.
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.
Yeah I think removing some of the conversions here is a good idea, for the moment this conversion to BigInteger makes the overflow check easier.
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 able to remove the conversion to BigInteger. Still thinking about the conversions to long
and ulong
, which are probably only necessary when we get a BigInteger value, to allow BitConverter to do what it wants.
conversion to BigInteger for the overflow check.
@@ -188,7 +188,7 @@ class TypecodeOps { | |||
return false; // All non-numeric types will not cause overflow. | |||
} | |||
|
|||
return value < minValue || value > maxValue; | |||
return PythonOps.Compare(value, minValue) < 0 || PythonOps.Compare(value, maxValue) > 0; |
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.
Unfortunately calling Compare
is probably even worse (performance wise) than converting to a BigInteger
. Also, since Compare
is equivalent to calling __cmp__
it will be removed from the codebase at some point.
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 tried benchmarking setting, and indeed Compare is much slower. The current change is only a little faster (~10%) than converting to BigInteger (possibly within the margin of error -- it wasn't a very empirical process), but I think it's a little cleaner than expecting a BigInteger in TypecodeOps. Here's what I did to benchmark:
import time
import array
def benchmark_memoryview(mv):
start_time = time.time()
for j in range(2000):
for i in range(4000):
mv[i] = i + j
end_time = time.time()
return end_time - start_time
mv = memoryview(array.array('i', [0]*4001)).cast('b')[1:16001].cast('i')
print('int unaligned', benchmark_memoryview(mv))
mv = memoryview(array.array('i', [0]*4000))
print('int aligned', benchmark_memoryview(mv))
Also, the aligned benchmark was super slow because _shape
was null, so I initialized it in the constructor.
Looking at the CI, it looks like the failures on Mono are weakref related? I would be fine with just skipping testing on Mono (by adding RunCondition=NOT $(IS_MONO) with a comment to AllCPythonCasesManifest.ini). |
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.
Looks fine to me overall. Did you want to get this merged in or was there more you wanted to do?
} | ||
|
||
public PythonList tolist() { | ||
CheckBuffer(); | ||
return _buffer.ToList(_start, _end); |
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.
Is this a spot where we could have a fast path with _matchesBuffer && _step == 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.
Added, but only tested against the two test_memoryview
tests.
Other than the fast track, I don't think anything comes to mind. The last piece is indexing at a tuple, but I think that should be another pull request, and I don't believe it's blocking anything for IronPython. Thanks for the patience and the helpful reviews with this one! I believe that the remaining things to do after this is merged are:
|
Thanks for the PR! |
... Kind of.
I have a couple pieces of the implementation that I think need revision, namely: