# What is a Mathematica packed array?

A simple sounding question with a few sub questions:

• What is the difference between unpacked vs packed array?
• Are packed arrays more space efficent, how much so?
• Are packed arrays more time efficient for certain types of access over the unpacked form?

Bonus:

Is it ever undesirable to make use of packed arrays, even if the data can fit?

-
Related: stackoverflow.com/q/8775372 – R. M. Mar 25 '12 at 21:49
Related: library.wolfram.com/infocenter/Demos/391 (R. Knapp) – Michael E2 Nov 28 '15 at 15:47

# Space efficiency

Packed arrays are significantly more space efficient. Example: Let's create an unpacked array, check its size, then do the same after packing it:

f = DeveloperFromPackedArray[RandomReal[{-1, 1}, 10000]];
ByteCount[f]
ByteCount[DeveloperToPackedArray[f]]

(*
320040
80168
*)


# Time efficiency

The difference seems to be how they are stored; packed arrays can only contain objects of the same type, so mma does not need to keep track of the type of each element. This can also speed up operations with them. Define

ClearAll[timeIt];
SetAttributes[timeIt, HoldAll]
timeIt[expr_] := Module[{t = Timing[expr;][[1]], tries = 1},
While[t < 1.,
tries *= 2;
t = AbsoluteTiming[Do[expr, {tries}];][[1]];
];
Return[t/tries]]


then

ClearAll[f, fpacked];
f = DeveloperFromPackedArray[RandomReal[{-1, 1}, 500000]];
fpacked = DeveloperToPackedArray[RandomReal[{-1, 1}, 500000]];

fpacked.fpacked // timeIt
f.f // timeIt

Sin[fpacked] // timeIt
Sin[f] // timeIt

(*
0.0001610173
0.01167263
0.00487482
0.01420070
*)


# Unpacking

To be warned of arrays being unpacked, you can do SetSystemOptions[PackedArrayOptions->UnpackMessage->True] or, in versions after 7, On["Packing"] (thanks to OleksandrR for pointing this out). The you see that eg Select unpacks: try Select[fpacked, 3] and a message is produced. Also assigning a value of different type to a packed array unpacks it: try fpacked[[2]] = 4 to see this.

This unpacking explains mysterious slowdowns in mma code most of the time for me.

It appears that it is twice as slow to address a single element in a packed vs an unpacked array:

ClearAll[f, fpacked];
f = DeveloperFromPackedArray[RandomReal[{-1, 1}, 500000]];
fpacked = DeveloperToPackedArray[RandomReal[{-1, 1}, 500000]];

fpacked[[763]] // timeIt
f[[763]] // timeIt
(*
4.249656*10^-7
2.347070*10^-7
*)


AppendTo is not faster:

AppendTo[fpacked, 5.] // timeIt
AppendTo[f, 5.] // timeIt
(*
0.00592841
0.00584807
*)


I don't know if there are other kinds of addressing-like operations that are faster for packed arrays (I doubt it but could be wrong).

# Aside

In the Developer context there are these names involving Packed:

Select[
Names["Developer*"],
Not@StringFreeQ[#, ___ ~~ "Packed" ~~ ___] &
]
(*
{"DeveloperFromPackedArray", "DeveloperPackedArrayForm",
"DeveloperPackedArrayQ", "DeveloperToPackedArray"}
*)


DeveloperPackedArrayForm does this:

ClearAll[f, fpacked];
f = DeveloperFromPackedArray[RandomInteger[{-1, 1}, 5]];
fpacked = DeveloperToPackedArray[RandomInteger[{-1, 1}, 5]];

DeveloperPackedArrayForm[f]
DeveloperPackedArrayForm[fpacked]
(*
{-1, -1, -1, -1, -1}
"PackedArray"[Integer, <5>]
*)


So, you could set \$Post = DeveloperPackedArrayForm and then packed arrays would be displayed in a special way. I am not sure if this has any other sideeffects (this has been suggested in this great answer by ruebenko).

-
As of version 7, one may use On["Packing"] as an alternative to SetSystemOptions[PackedArrayOptions->UnpackMessage->True]. Also, AppendTo will not be appreciably faster for packed arrays in most cases because the great bulk of its runtime consists of copying the array. At least this is done without unpacking unless absolutely necessary (therefore probably using optimized memcpy). – Oleksandr R. Mar 26 '12 at 0:11
I think On["Packing"] may be one of the most useful things I've come across on this site. Thanks! – Pillsy Jun 19 '13 at 19:30

### The difference

Packed arrays give you pretty much an access to a direct C memory layout, where the arrays are stored. Unpacked arrays reference arrays of pointers to their elements. This explains most of the other differences, in particular:

• Space efficiency: if you look at how much space is required for packed arrays, you see that it is exactly the amount you'd need in C
• Limitation to be rectangular: this allows to allocate arrays as contiguous blocks of memory, and perhaps use fast operations for array copying etc (such as memset, memcpy, or whatever custom analogs of them may exist in M implementation).

### Runtime efficiency

Packed arrays by themselves would not bring to the table much except space efficiency. However, in addition to the new data structure, most fundamental functions have been internally overloaded to automatically use their specialized and much more efficient versions when arguments are packed arrays. Among these functions: Join, Tally, DeleteDuplicates, UnitStep, Clip, Unitize, Pick, Part,Transpose, Partition, etc.

This is a kind of a partial replacement of compilation in an interpreted environment. Some important things related to this:

• Most numeric functions are Listable. This Listability is often not distinguished from the high-level one, where you can assign the Listable attribute to any function you write. While conceptually they serve the same purpose, being Listable means a different thing for numeric built-in functions in terms of implementation: it tells them that, given a packed array, they should use a specialized low-level version. This is the reason for huge speed-ups, because you effectively compile this part of the code.

• Most built-in functions which take and process packed arrays, also output packed ararys, which provides means for composition.

• Compile operates on packed arrays and produces packed arrays. Most common iteration functions such as Map, Table etc often auto-compile the functions they iterate, thus also produce packed arrays. This adds a lot, since the user is able to extend the set of fast (packed-array based) functions by using Compile. Since M8, the user is also able to produce Listable compiled functions, in the same sense as numeric Listable functions.

• Sparse arrays use packed arrays internally to store their data

The main idea of all this is to operate on large chunks of data at once, and avoid the main evaluator by pushing most of the work to the kernel. As I said, this IMO can be viewed as a sort of a partial compilation technique. I just want to stress once again that for this to work, the most important part is a tight integration of packed arrays into the core language, which affects many functions. All these functions have specialized low-level versions which are used when packed arrays are supplied to them. Because of the rectangular layout of the arrays, they map directly on native C arrays, so these specialized implementations can be very fast.

In addition to the observations of @acl, I just want to stress that addressing measured in isolation seems not really that important (the twofold difference is most likely due to the extra pointer dereferencing, although I may be wrong). The point IMO is that packed arrays are effective when used with an entirely different programming style, where explicit individual indexing is avoided as much as possible (except possibly inside Compile), and instead the code is rewritten in such a way that this indexing is done internally by built-in functions, at a much lower level.

### Limitations

• As mentioned already, arrays must be rectangular and of the same native type (Integer, Real, or Complex)
• Not all functions benefit from packed arrays. One notable example which does not, is Sort (and also Union, Complement, Intersection, Ordering) with a default comparison function.

### When to use

Actually, whenever you can. I can't recall any case off the top of my head where the use of packed arrays would hurt (if they can be used). Just one hypothetical scenario comes to mind: you store a large amount of data in a packed array, but then somewhere in your code it gets unpacked and eats up all your memory. However, while it is stated in the documentation that computations on packed arrays would always produce the same results as on identical unpacked ones, there are probably corner cases like this one, where this is not so. It seems however that such cases are, so to speak, of measure zero.

One useful trick which isn't emphasized enough is that often you can store your data very space-efficiently even when the main array can not be packed, but its elements can. Given such a list as unpacked, you can Map DeveloperToPackedArray on it, which may lead to very siginificant savings, both in terms of run-time and memory efficiency. One example of such use is here.

In general, when you see the recommendation to "vectorize the problem" or "use vectorized operations" for speed, this is exactly about using packed arrays. Various solutions for this question (except mine) are good examples of such vectorized use. There are plenty of other similar ones here on SE, on SO and MathGroup. One example which I find interesting and somewhat standing out is this one, where I used packed arrays to pack a small matrix of positions, and this still lead to a dramatic speed-up because that matrix was used to extract huge numbers of elements from a list at once, and Extract is also optimized on packed arrays - so, in some cases packing of even small arrays can be beneficial.

This illustrates once again my main message: the big deal is not just packed arrays as a stand-alone data structure, but a different programming style possible when all relevant ingredients are packed. It is this style which leads to huge performance boosts, not just packing by itself.

-
good point about them being useful for a different style, and useful discussion :) +1 – acl Mar 25 '12 at 23:37
@acl Thanks. Your answer is pretty good too, I voted for it. And I also noticed a rather untypical for your answering style length and organization of it. This seems painfully familiar, and apparently contagious :) – Leonid Shifrin Mar 25 '12 at 23:42
thanks. About the style, I thought I'd up my game :) I find this organization useful in other people's answers so I try to do the same – acl Mar 25 '12 at 23:45
please note how flexibly I am entering into extended discussions in comments, mere hours after proclaiming their absence being the sole differentiator from other online resources! – acl Mar 25 '12 at 23:46
@acl Oh yes, I do note. Actually, people do want to get engaged in discussions, and comments on SE is a backdoor for that. I can imagine that SE "doesn't like this" (and there is some explicit evidence, such as automatic invitations to switch to chat), but it has to live with that. Will leave now, to avoid such an invitation and get some sleep - it is 4 a.m. here. – Leonid Shifrin Mar 25 '12 at 23:51

I would like to point out that Listable in a pure Function effectively unpacks the array, and makes it much slower than Map for pure Functions.

Downvalues always unpack so SetAttributes[f, Listable] doesn't affect performance there.

The bottom line is that if one wants to use user defined listability it must be inside a compiled function, otherwise use Map

data = RandomReal[1, 5 10^6];

AbsoluteTiming[ DeveloperPackedArrayQ[Function[u, u^2, Listable]@data]]

{4.54275,False}

AbsoluteTiming[ DeveloperPackedArrayQ[Function[u, u^2, Listable]/@data]]

{0.177237,True}
`
-