I have a need to work with very large arrays of data in Mathematica. The length of the list is not known upfront. The typical size is a few hundred thousands elements and could easily go beyond a million elements in some cases. There are two major types of lists I need to work with: 1. list of real numbers, 2. list of some (different) symbolic expressions.
While the list of real numbers is internally converted into packed array and does not seem to cause much performance issues, it is the second type of list (very long list of some different symbolic expressions), which is causing a major problem. It turned out that access time (get or set) to a single element of such lists grows linearly (if not faster) with the length of the list.
I wrote a simple test to evaluate average access speed per element for such lists along with the workaround to deal with the issue: https://www.dropbox.com/s/7j9qlppmrsrmjug/CLM_DynArray_Test_204__008.nb?dl=0 .
I ran the test on Mathematica 7, 9, and 10. Version 7 does not have get issue but still has set issue and both versions 9 and 10 have both get and set issues. This definitely looks like an error in internal Mathematica implementation unless I missed something. I would appreciate any help with that problem.
Here is the excerpt from the code above to illustrate the problem:
ClearAll["Global`*"];
nnnList = {1000, 1500, 2000, 3000, 4000, 5000, 7000, 10000, 15000, 20000, 30000,
40000 , 50000, 70000, 100000};
MinReperition = 5;
MaxLength = MinReperition*Max[nnnList];
CreationTiming[nnn_?IntegerQ, useVariables_?BooleanQ] := CreationTiming[nnn,useVariables, True];
CreationTiming[nnn_?IntegerQ, useVariables_?BooleanQ, useRepetitions_?BooleanQ] :=
Module[
{timing, noOfRepetitions, repetitionCount},
noOfRepetitions = If[useRepetitions, Ceiling[MaxLength/nnn], 1];
timing = 10^6*Mean[
Table[
If[useVariables,
Timing[vars = Table[{ToExpression["var" <> ToString[ii]]}, {ii, 1, nnn}];][[1]],
Timing[vars = Table[RandomReal[], {ii, 1, nnn}];][[1]]
],
{repetitionCount, 1, noOfRepetitions}
]
];
Return[timing];
];
AccessTiming[nnn_?IntegerQ, useVariables_?BooleanQ] :=
Module[
{timing, x, noOfRepetitions, repetitionCount},
noOfRepetitions = Ceiling[MaxLength/nnn];
CreationTiming[nnn, useVariables, False];
timing = 10^6*Mean[
Table[Timing[Do[(x = vars[[ii]]), {ii, 1, nnn}];][[1]],
{repetitionCount, 1, noOfRepetitions}]
];
Return[timing];
];
plotOptions = {PlotRange -> All, Frame -> True, GridLines -> Automatic};
Print["Access time per Real Table element in microseconds."];
Print[DiscretePlot[AccessTiming[nnn, False]/nnn, {nnn, nnnList}, Evaluate[plotOptions]]];
Print["Access time per variable Table element in microseconds."];
Print[DiscretePlot[AccessTiming[nnn, True]/nnn, {nnn, nnnList}, Evaluate[plotOptions]]];
nnnList is the list of lengths to be tested for access speed.
For small list lengths it is necessary to repeat the test more times so that to obtain a better average result. For the largest list length the test will be performed MinReperition times.
CreationTiming measures time in microseconds, which is necessary to create a Table of length nnn. If useVariables [Equal] False, then the function populates the Table with random real numbers. If useVariables [Equal] True, then the function populates the Table with generated symbolic variables (var1, var2, var3, etc...). useRepetitions [Equal] True (default) will result in multiple runs. useRepetitions [Equal] False will result in a single run.
AccessTiming measures time in microseconds, which is necessary to consecutively get all the elements from a Table of length nnn.
DiscretePlot plots the time per element to access all elements. As the time per elements grows at least linearly the total time to access all elements grows at least quadratically.
arr = Table[RandomInteger[10, 5].x^Range[5], {1000000}]; Table[AbsoluteTiming@Do[arr[[i]], {i, Length[arr]}], {10}]
I see a linear increase asarr
grows. However, the very first time to iterate through the array is much slower than subsequent times. $\endgroup$AbsoluteTiming
by the number of calls. If I do so I get equal timings independently of the length of the list:Table[arr=Table[RandomInteger[10,5].x^Range[5],{nnn}];Mean[Table[AbsoluteTiming[Do[arr[[i]],{i,Length[arr]}]][[1]]/Length[arr],{10}]],{nnn,{10000,100000,1000000}}]
. $\endgroup$