Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/15/2013 in all areas

  1. Hi Mike, I got 92% for requirements coverage. I don't think they expect you to be near 100% but if you only hit 70% you'll need to do well at the architecture IMO it would be easier to get points from the requirement tags. Hope this helps
    1 point
  2. But you would need to acquire the mutex in every read and every write just in case a resize tried to happen. The only time when you know only one operation is proceeding is when the buffer is empty (all current readers have read all enqueued items) and the write operation is called (assuming we figure out a way to prevent multiple parallel writers on the same refnum, which is solvable if we can find some way to expose the atomic compare-and-swap instruction, which might require changes for LV 2014 runtime engine). That's the only bottleneck operation in this system. In that moment, the write operation could resize the read buffer and then top-swap a new handle for the read indexes into a single atomic instruction into the block of pointers. So if you have the Write VI able to have a mode for Resize (and likely for Uninit, as I mentioned in my previous post) then you could resize. I don't see any other way to handle the resize of the number of readers. Anyone else attempting to swap out the pointer blocks faces the race condition of someone trying to increment a block and then having their increment overwritten by a parallel operation.
    1 point
  3. I spent an hour or so last night starting to code my own solution and got as far as muddling with pointers for indexing then stopped for the night. It occurred to me while I was mulling the problem through my head are the memory manager calls re-entrant? If I'm going about working directly with pointers using functions such as DSNewPtr, DSNewPClr, MoveBlock, and DSDisposePtr much as Shaun did in an effort to circumvent the lock mechanisms behind DVRs and FGVs, is it even possible to have two MoveBlock calls executing at the same time? Obviously this demands that each call be made from a different thread and the CINs aren't configured to use the UI thread, but the documentation is pretty much headers only as far as I can tell and doesn't really indicate either way. I'm hoping to quantify whether there are any gains to be made by leaving behind the DVR in favor of lower level memory management of the reader/writer indices. I'm still going to be keeping the buffer proper as a native LabVIEW implementation (DVR) since I see no other way to be able to store non-flat structures by poking about memory directly without invoking expensive operations like flattening/unflattening. My hypothesis any gains that may be had will be modest enough to not warrant the increased CPU load of polling the indexing loops. Just after posting this I realized the re-entrant nature of MoveBlock is probably irrelevant if it is only being used for indexes. These bits of data are so small there's likely no measurable difference in practice if the calls were forced serial or not. Might be relevant if playing with larger structures, but as I said, I plan on keeping the buffer in native LabVIEW. It will still be interesting to test my hypothesis though to see if dancing around the reference locking mechanism saves anything. Yes, I'm a scientist, hypothesis testing is what I do...
    1 point
  4. There are a couple things I do that make your specific problem a non-issue for me. (Factory method image copied from my post above.) First, I've ditched the rule of wrapping all sub-vi code in an error case structure. (Blasphemy! Burn me at the stake!) About 9 months ago AQ and I had a discussion about this. He termed it, "hyper-reactionary error handling" and talked about the impact it has on the cpu's resources. He said, These days I almost never wrap an entire code block in an error case. Many of my sub vis don't have an error case at all. The questions I ask myself when deciding about an error case are: 1. Do I need the sub vi outputs to be different if there is an error on the input terminal? If so, then I'll use a switch prim or case structure somewhere on the block diagram, though it may not wrap the entire code block. 2. Will the sub vi exit significantly faster by skipping the code on the block diagram when an error is on the input terminal? Yes? Then I'll wrap at least that section of code in an error case. The code I'm looking at here are things like For Loops containing primitive operations that don't have error terminals. String operations, array operations, cpu intesive math, etc. If a sub vi or prim has an error input terminal I don't worry about it's processing time. It will respond to the error input terminal appropriately and I assume (for simplicity) that its execution time = 0 if there is an error in. In the diagram above I'm doing a lot of data compilation to gather the information necessary to create a GutStep object. However, the only primitive operations on this bd are Search Array and Index Array. Is the time saved in those rare instances where there is an error in worth the cost of error checking the other 99.9% of the time when there isn't? Nope, not in my opinion. In fact, this entire block diagram and all the sub vis execute exactly the same code regardless of the error in condition. (I know the sub vis ignore the error in terminal because of the green triangle in the lower left corner.) But low level performance isn't the main reason I've ditched it. I found it adds a lot of complexity and causes more problems than it solves, like the issue you've run into. You've wrapped your accessor method code in an error case because that's what we're "supposed" to do. Why does the accessor method care about the error in terminal? Is unbundling a cluster so expensive that it should be skipped? Nope. Is it the accessor method's responsibility to say, "I saw an error so I'm not returning a real queue refnum because some of you downstream vis might not handle it correctly?" Nope. Each sub vi is responsible for it's own actions when there is an error on its input terminal. In pratical terms that means my accessor methods never have an error case. They set/get what's been requested, each and every time. My object "creator" methods almost never have an error case, also passing the error wire through. Most of my "processing" method don't have an error case either. In the diagram I have 3 different "Lookup" methods. Each of those encapsulates some processing based on the inputs and returns an appropriate output. None of them change their behavior based on the error in terminal's state. However, each of the three Lookup methods are capable of raising an error on their own (I know this because the lower right corner doesn't have a colored triangle,) which would ulitmately result in an invalid GutStep object being sent out. Is it the factory method's responsibility to try and prevent downstream code from accidentally using an invalid GutStep object? Nope. It's the factory method's responsibility to create GutStep object to the best of it's ability. If an error occurs in this method or upstream of this method that might cause the data used to create the GutStep object to be something other than expected, this method lets the calling vi know via the error wire. It's the responsiblity of the calling code to check the error wire and respond according to it's own specific requirements. That's a little bit more about my general approach to error handling. It's more geared around responsibilities--who should be making the decision? (Responsibility-based thinking permeates all my dev work.) I've found that, in general, much of the time I don't really care if there's an error on the wire or not. Many of the primitive functions and structures already know how to handle errors. What do I gain by blindly adding more layers on top of it? Complexity in the form of more possible execution paths, each of which needs to be tested or inspected to verify it is working correctly. Thanks, but I'll pass. ------------ So, now that I've talked your ear off, it sounds like you can solve your immediate problem by removing the error case from your accessor method. All the above is justification for doing that. Of course, that change may cause problems in the rest of your code if you use the <Not a Refnum> output for branching execution flow.
    1 point
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.