Testing for empty arrays and clearing arrays
Monkey Forums/Monkey Programming/Testing for empty arrays and clearing arrays
| ||
Hi all, I'd like to check that my understanding of the following points are correct, thanks! Consider the following code: Local test:TParticlePhase[] If not test Then Print "Test is null" Local phase:TParticlePhase = New TParticlePhase test = test.Resize(test.Length + 1) test[test.Length - 1] = phase Print test.Length If not test Then Print "Test is null" test =[] If not test Then Print "Test is null" test = test.Resize(test.Length + 1) test[test.Length - 1] = phase Print test.Length output is as expected: Test is null 1 Test is null 1 I'm aware that an array is not an object, therefore you cannot test to see if it is null e.g. if test = null then However I discovered that you can use: if test then and you can also use: if not test then which has the same effect as: if test.Length()=0 then So how is that working then? As for clearing an array I was using this to create a zero length slice test = test [0..0] but then I discovered other people were using test = [] which seems to work fine in the code above. Am I doing it right? Any feedback welcome. Thanks! |
| ||
I assume there's an implicit conversion of array to bool with a non-empty array returning true and an empty one returning false. As for the second question: Local a:Int[] = [] ' fine Local b:Int[] = a ' "" Local c:String[] = [] ' "" Local d:String[] = c ' "" Local e:String[] = a ' nope It will accept [] to initialise an empty array, but it is not actually creatijng [] and setting your array to that. Both your methods are fine. Depending on optimisations, setting it to [] might be faster. Note that you cannot actually clear an array - whichever method you choose, you are creating a new zero-length array, and setting test to that. |
| ||
the problem is with the If statement this should be valid but is not: Local a:int[] = [] if not a then ' is ok if a = [] ' not ok <------ fine, I'll buy that. If a = Null Then ' not ok <---------- but I don't think this should be an error. |
| ||
>So how is that working then? Pretty much how you imagined. ""If arr" implicitly casts to Bool and if you look in the trans code the Bool cast for an array is: If ArrayType( src ) Return Bra( t+".Length()!=0" ) The same thing occurs for strings. >Am I doing it right? There's nothing wrong with what you're doing on a line by line basis. Algorithmically, if you're blowing away large arrays of object references then you may well be risking GC performance issues though. Stylistically I'm not a big fan of the implicit Bool casting. I think it's unclear, and that lack of clarity produced this very thread, but it's valid Monkey and I do find myself lazily using it. |
| ||
Thanks all, very interesting. @muddy_shoes: So you'd prefer if test.Length()=0 instead of the implicit Bool casting? I was wondering if using Length() might be slower (I'm using it a large number of times per frame as part of my particle system). Although I admit it probably won't make much difference (yes I could test it). As for killing off large arrays of object references, the only way round that would be to use object pooling (which I do for my particles and emitters) right? |
| ||
So you'd prefer if test.Length()=0 instead of the implicit Bool casting? I was wondering if using Length() might be slower (I'm using it a large number of times per frame as part of my particle system). Stylistically I'd prefer it because that's what it really means. In terms of performance the difference is somewhere from 0 to not a lot depending on platform. To be honest I'd have thought it would be zero for everything, seeing as it's functionally equivalent, but for some reason actual calls to the Array Length method go through different translation paths than the Bool cast on some targets*. A call to Length on Java and C# incurs a null test in the native code even though the array instance should never be null and uses library Array utility functions instead of the instance method. Not sure what that's about but it seems that using "If arr.Length() = 0" might actually be marginally slower with the way Monkey works currently (Would need testing though. It could all come out equal in the final compiled code.). That said if you're performing this test often enough in a small enough loop for the difference between those two calls to make a difference then avoiding the calls altogether by tracking whether you've initialised the array in a separate boolean would seem to be the desirable option. As for killing off large arrays of object references, the only way round that would be to use object pooling (which I do for my particles and emitters) right? Right. You will also still have the GC impact of the array itself. I'd guess you're unlikely to be planning to instantiate and throw away enough large arrays to be a big deal but it's worth remembering. |
| ||
If you are concerned about the GC impact of the array, use a Stack instead. |
| ||
Okay, I got curious enough to try a little test. I found that in XNA "If arr" is about 40% faster than "If arr.Length() <> 0" and on Android "If arr" is a fairly crazy 20+ times faster than "If arr.Length() <> 0". It looks like a completely avoidable problem in trans to me, but I may be missing something. Anyway, the length check is taking ~1.5 microseconds on my crappy Android phone, which would add up to something tangible if you were doing thousands per frame. So, bonkers though it is, using "If arr" actually could make a performance difference. |
| ||
Hi there I did an experiment to find out what is going on. I have this code: Local test:TestClass[] If Not test Then Print("test is Null") Then I look at the cpp file that the transcompiler generated and the condition statement had been: if(!((t_test).Length()!=0)){ DBG_BLOCK(); bbPrint(String(L"test is Null",12)); } So then this means that if the condition would be to simplified would be If test.Length = 0 Then Print("test is Null") So thus transcompiler would do if(t_test.Length()==0){ DBG_BLOCK(); bbPrint(String(L"test is Null",12)); } Very interesting, isn't it? |
| ||
Yes very interesting. What does the faster (as discovered by muddy_shoes) "if test" compile into? Does this all mean if would be faster to do this to test for an empty array? Seems weird but if it's faster I'll take it. if test 'do nothing else 'code that you wanted to do if the array is empty |
| ||
The performance difference is that (in Java and C#) "if test" goes through a bool cast and gets converted to a direct reference to the underlying language array.length property. Calls to Monkey's Array.Length() result in a call to a function that has significant overhead, including an unnecessary null check. Funnily enough, after I wrote the post about I remembered that I found this issue before and provided Mark with a fix in a pull request -- https://github.com/blitz-research/monkey/pull/5. As is his habit, he declined to take the code and said he'd do it manually. Then, as is often the result of choosing to step outside systems for tracking change, the change apparently got lost. |
| ||
Awwwwwwwww. Well thanks anyway. |