X

NV5 Geospatial Blog

Each month, NV5 Geospatial posts new blog content across a variety of categories. Browse our latest posts below to learn about important geospatial information or use the search bar to find a specific topic or author. Stay informed of the latest blog posts, events, and technologies by joining our email list!



Mapping Earthquake Deformation in Taiwan With ENVI

Mapping Earthquake Deformation in Taiwan With ENVI

12/15/2025

Unlocking Critical Insights With ENVI® Tools Taiwan sits at the junction of major tectonic plates and regularly experiences powerful earthquakes. Understanding how the ground moves during these events is essential for disaster preparedness, public safety, and building community resilience. But traditional approaches like field... Read More >

Comparing Amplitude and Coherence Time Series With ICEYE US GTR Data and ENVI SARscape

Comparing Amplitude and Coherence Time Series With ICEYE US GTR Data and ENVI SARscape

12/3/2025

Large commercial SAR satellite constellations have opened a new era for persistent Earth monitoring, giving analysts the ability to move beyond simple two-image comparisons into robust time series analysis. By acquiring SAR data with near-identical geometry every 24 hours, Ground Track Repeat (GTR) missions minimize geometric decorrelation,... Read More >

Empowering D&I Analysts to Maximize the Value of SAR

Empowering D&I Analysts to Maximize the Value of SAR

12/1/2025

Defense and intelligence (D&I) analysts rely on high-resolution imagery with frequent revisit times to effectively monitor operational areas. While optical imagery is valuable, it faces limitations from cloud cover, smoke, and in some cases, infrequent revisit times. These challenges can hinder timely and accurate data collection and... Read More >

Easily Share Workflows With the Analytics Repository

Easily Share Workflows With the Analytics Repository

10/27/2025

With the recent release of ENVI® 6.2 and the Analytics Repository, it’s now easier than ever to create and share image processing workflows across your organization. With that in mind, we wrote this blog to: Introduce the Analytics Repository Describe how you can use ENVI’s interactive workflows to... Read More >

Deploy, Share, Repeat: AI Meets the Analytics Repository

Deploy, Share, Repeat: AI Meets the Analytics Repository

10/13/2025

The upcoming release of ENVI® Deep Learning 4.0 makes it easier than ever to import, deploy, and share AI models, including industry-standard ONNX models, using the integrated Analytics Repository. Whether you're building deep learning models in PyTorch, TensorFlow, or using ENVI’s native model creation tools, ENVI... Read More >

1345678910Last
8179 Rate this article:
No rating

On the dangers of keyword inheritance

Anonym

IDL gives you the tools to write highly efficient code very quickly. It also gives you many, many opportunities to write very, very bad code. Here's one problem:

"I love _EXTRA.  I use it everywhere in my code to cut down on my typing, but my applications run more slowly and sometimes I run out of memory for reasons that I don't understand. What's up with that?"

Imagine a chain of routines.  Here, A calls B, then B calls C.

pro C, FOO = foo, BAR = bar
   HELP, foo, bar
end 

pro B, Z = z, _EXTRA =_e2
   HELP, z
   C, _EXTRA = _e2
end 

pro A, _EXTRA = _e1
   B, _EXTRA = _e1
end

(NB: I've left out any error handling to keep the code simple.  Remember to execute RETALL if you encounter any runtime errors.) The routine A may be, say, a data ingest routine, B may be a data conditioning routine, and C may be a display routine. As my chain of procedures is called, each routine pulls off the keywords it needs then passes anything unknown down to the next routine in the chain. (NB: We're ignoring the case where identical keywords may appear in multiple routines; that's a bookkeeping nightmare left for another day.) In theory, I've made my life a lot easier by using _EXTRA to package up all unneeded keywords in one routine and passing them as a bundle down to the next routine in the chain. There's a lot less typing than if I'm explicit about including all the keywords in B that are used by C, or all those in A that are used by B and C. Look how snappy this is!  I can use the keywords in any combination to affect the behavior of routines way down in my call chain:

IDL> A
IDL> A, FOO = 20
IDL> A, Z = 10, FOO = 20, BAR = 3

Wasn't that convenient?  IDL's the best thing EVER! Let's try something a little different now.  What if FOO is a big array?  This could, for example, be a VERT_COLORS array eventually sent to the SURFACE function or the AUXDATA keyword to IDLgrTessellator. (NB: The size of the array in the following example that will test your system's limits will be highly dependent on your host's configuration, but the takeaway message will be common across all platforms. If this array size exhausts your memory, try something smaller.)

IDL> foo = FLTARR(10000, 10000)

First, let's ignore the routines A and B for the moment and just pass the variable ‘foo’  to C instead of A.

IDL> C, FOO = foo

Okee dokee!  No issues. Let's pass this array as a keyword that'll be passed silently through B but will be picked up in C.

IDL> A, FOO = foo

Did you run out of memory?  If not, did you see an unexpected delay before the command prompt returned? Do you now think IDL is the worst thing ever? Since the routines A and B are essentially no-ops in this case, on the surface it seems like we're basically just calling

IDL> C, FOO = foo

That call alone is not slow and doesn't chew up our memory, so what's going on here? Let's throw a call to this new routine in each.

pro SHOWMEM
   routine = (SCOPE_TRACEBACK(/STRUCTURE))[-2].Routine
   PRINT, 'memory use at start of routine ' + $
      routine + ' = ' + STRTRIM((MEMORY())[0], 2)
end

This little utility is going to show us how much memory IDL has allocated when it's called from another routine, printing out the name of the routine as well. Add a call to this routine at the start of A, B, and C:

pro C, FOO = foo, BAR = bar
   SHOWMEM
   HELP, foo, bar
end 

pro B, Z = z, _EXTRA =_e2
   SHOWMEM
   HELP, z
   C, _EXTRA = _e2
end 

pro A, _EXTRA = _e1
   SHOWMEM   B, _EXTRA = _e1
end

Call A again:

IDL> A, FOO = foo

Can you see what happened here?  With each call, the memory allocated essentially increased by the size of our variable named ‘foo’.  Zoinks! This isn't a coincidence. Why is this? _EXTRA isn't just a keyword.  It's a mechanism that copies the contents of unconsumed keywords in a procedure or function call into an anonymous structure. Another way to describe this is to say that _EXTRA passes copies of data by value. Add some HELP calls to the routines to see the contents of our _EXTRA variable at each step.

pro C, FOO = foo, BAR = bar
   HELP, foo
end 

pro B, Z = z, _EXTRA =_e2
   HELP, /STRUCTURE, _e2
   C, _EXTRA = _e2
end 

pro A, _EXTRA = _e1
   HELP, /STRUCTURE, _e1
   B, _EXTRA = _e1
end

This time, let's execute it with an argument to the Z keyword that will be consumed by the routine B.

IDL> A, Z = 5, FOO = foo

First, notice that the contents of our _EXTRA variables, ‘_e1’ and ‘_e2’, are different in the scope of each routine. In A, both Z and FOO have been copied to the structure ‘_e1’. When routine B is called from A, before it executes any instructions in the procedure, IDL's interpreter extracts the contents from the _EXTRA structure into named keywords it knows about. It then packages the remaining  keywords into a new _EXTRA keyword by copying the values before executing the code in the routine. The important thing to notice is that when we're in routine B with ‘_e2’, the variable ‘_e1’ is still in scope in routine B. Therefore, ‘_e1.FOO’ is one copy of ‘foo’ in the $MAIN$ scope and ‘_e2.FOO’ is another copy of ‘foo.’ When you finally make it into C, the local keyword variable ‘foo’ is yet another copy. A solution that is useful in many cases is to simply replace _EXTRA in the procedure definitions with _REF_EXTRA, the "by-reference" mechanism for passing keywords.

pro C, FOO = foo, BAR = bar
   SHOWMEM
   HELP, foo, bar
end 

pro B, Z = z, _REF_EXTRA =_e2
   SHOWMEM   C, _EXTRA = _e2
end 

pro A, _REF_EXTRA = _e1
   SHOWMEM   B, _EXTRA = _e1
end

Call A again:

IDL> A, FOO = foo

Notice that memory didn't increase in any egregious way between the procedure calls.  Don't you think IDL is the best thing ever again? A general discussion on this topic is outside the scope of this blog post, but suffice it to say that _REF_EXTRA passes a list of variable names from one routine to the next rather than their contents.  Via the _REF_EXTRA mechanism, IDL’s interpreter knows that it needs to look up one or more levels in the call stack for the actual data storage. You may find that you are limited in the extent to which _REF_EXTRA will save your hide, however, since not all of IDL's internal library routines are set up to use _REF_EXTRA. Also, since _REF_EXTRA is pass-by-reference, modifying the contents of variable ‘foo’ in C would be reflected in a change at the B, A, and $MAIN$ levels, which you may or may not intend.   FOO is “read/write”. This would not be the case if the variable is passed via _EXTRA.  Any changes to the copy of ‘foo’ in C would be discarded when the routine exits, in this case.  FOO is “read only”.

Please login or register to post comments.