Iterating through large collections is very slow (non linear).
Whenever I have a need to iterate through a large collection (such as pageItems) the iteration is very very slow, and the larger the collection is the more slower it gets. I would expect this to be a linear behavior, but it's not. If a collection's size doubles, it takes more than double the time to iterate through it.
-
Marian Becher commented
As already mentioned by others the indexed access into a typed collection (`doc.pageItems[i]`, `layer.pathItems[i]`, …) is not O(1). The cost of a single `[i]` grows with `i`. So the obvious forward loop over a whole collection is O(N²) and becomes unusable on large documents.
My Environment:
Illustrator `30.3`, Windows, ExtendScript / CEP.The workaround I'm using:
since `uuid` / `Document.getPageItemFromUuid()` were added (~v24 / 2020), and UUIDs appear to be handed out as sequential integers, I can ignore the collection and walk by UUID. `getPageItemFromUuid(String(n))` appears to be O(1), so the traversal is O(N):Example Code:
(function () {
var doc = app.activeDocument;
var expected = doc.pageItems.length; // target count
var HARD_CAP = 268435456; // 2^28 safety net
var t0 = new Date().getTime(), yielded = 0, cursor = 0;while (yielded < expected && cursor < HARD_CAP) {
var item = null;
try { item = doc.getPageItemFromUuid(String(cursor)); } catch (e) {}
cursor++;
if (item == null) continue; // empty / deleted slot
// ...do work with `item`...
// Note: I had to add additional test to filter out items from other documents as well as well as Layers etc...
yielded++;
}
$.writeln("UUID walk: " + yielded + " items in " +
((new Date().getTime() - t0) / 1000) + "s (cursor=" + cursor + ")");
})();1. Is there an official, supported O(N) way to enumerate every `PageItem` at any depth that I've missed?
2. Is the sequential-integer-UUID walk safe to rely on, or is it an implementation detail that can break — non-zero-based ranges, gaps beyond plain deletions, non-integer UUIDs after edits/undo/redo, copy-paste between docs? Today I skip empty slots, stop at `pageItems.length`, and cap the cursor.Caveat others may hit: `getPageItemFromUuid` returns items from other open and even closed documents (UUIDs are app-global). I filter by walking `.parent` up to the owning `Document` and comparing.
-
Роман Волков commented
Adobe Illustrator is running slower and slower with each new version :(((
-
Vitaly Polyakov commented
I have such a problem. I checked Illustrator versions 16.2.0, 26.4.1, 27.0 - the problem is reproduced.
For example generate a file with a lot of objects using generate_rectangles.jsx
Then run test_collection.jsx
I got the following results:
Access time to pageItems[0] - 0.002 sec
Access time to pageItems[100000] - 0.02 secgenerate_rectangles.jsx
------------
var count = 100001;
for (var i = 0; i < count; i++) {
app.activeDocument.pathItems.rectangle(0,0,100,100);
if (i % 100 == 0)
$.writeln(i);
}
------------
test_collection.jsx
------------
var pi = activeDocument.pageItems;
for (var i = 0, len = pi.length; i < len; i+=100) {
$.hiresTimer
pi[i].name = i;
if (i % 100 == 0){
var time = $.hiresTimer/1000000;
$.writeln(i+": " + time.toFixed(4) + " seconds")
}
}