Please help me debugging loading time of actions

Hello guys,

multiple of my users are reporting that it takes very long (multiple minutes) for listing actions in my plugin ActionBrowser 2.
I can’t reproduce it on my Mac. Maybe it’s a Windows issue that was introduced with some of the latest Photoshop updates.

Could you please copy and paste the following code in the debugging console and copy the result as a reply to this thread?

const actionTree = require("photoshop").app.actionTree;
const os = require("os");
const actions = [];
const start = performance.now();
actionTree.forEach(set => {
    set.actions.forEach(action => actions.push(action))
});
const end = performance.now();
const output = {actions: actions.length, timeTaken: end - start, os: os.platform(), osVersion: os.release(), psVersion: require("uxp").host.version};
output;

It should look like this:

The script shows the number of actions installed the time in ms it took to get the actions. It also shows the OS, OS version and the PS version. Thanks! :pray:

I suspect it might be related to this.

I remember it being very slow like 1 or 2 months ago. I used multiGet to solve my problem.
image
image
image

1 Like

I ran the test multiple times and created dummy actions between runs:

1st run

{actions: 1, timeTaken: 76, os: "win32", osVersion: "10.0.22621", psVersion: "24.3.0"}

2nd

{actions: 14, timeTaken: 44, os: "win32", osVersion: "10.0.22621", psVersion: "24.3.0"}

3rd

{actions: 34, timeTaken: 31, os: "win32", osVersion: "10.0.22621", psVersion: "24.3.0"}

4th

{actions: 138, timeTaken: 61, os: "win32", osVersion: "10.0.22621", psVersion: "24.3.0"}
1 Like

Thanks guys.
Hm, there are some inconsistencies but nothing crazy. When they don’t have installed thousands of actions it shouldn’t take multiple minutes.
Maybe the issue is somewhere else in my code. I hate debugging without being able to reproduce the issue :wink:

1 Like

So then try this: Application Performance Monitoring & Error Tracking Software :smiley: It will send errors for you and if you add source code + source map you can see where error happened in customer machine. Also you can add your own “transactions” meaning that you can sample plugin performance and different tags e.g. number of actions, OS, PS version, and others… but make sure that users will approve these statistics.

My results:

{actions: 179, timeTaken: 1844, os: "win32", osVersion: "10.0.19044", psVersion: "24.4.1"}'

That seemed reasonably quick.

I wonder if excess time to execute is related to:

  1. The number of actions the user has. Some people have tons of them. Or
  2. The use of the API to get the actions instead of using batchPlay.

1.8s is not that quick. Also, time increase can be non-linear but exponential as the number of actions grows.

Yeah, I guess that would seem a bit laggy if I were waiting for it to show up on screen in some type of UI.

I received more information from a user who found a very strange workaround:

Just an FYI. Stumbled upon a solution (that works for me, and possibly others). Tried to run plug-in today and again took 6.5 minutes to load. I found this work around accidently while using the History log, and works consistently. I know that it sounds ridiculous, but if I start the plug-in and then go to the History Brush Log and delete the entry marked “open”, the second the entry hits the garbage can in the History Log, the plugin loads. I have done this consistently, restarting PS every time prior. But, if you open PS and delete the “open” entry prior to activating the plug-in, it doesn’t give the same result. The plugin still sits a long time before loading. Go figure. The plugin must be open prior to doing this, with the msg “loading actions” showing. Any other state will not work. THEN delete the “open” entry. Hope this works for anyone else experiencing the same issue.

He says everything worked fine on Windows 10. Now he is on Windows 11. But I think the problem appeared in the latest PS versions and has less to do with the Windows version. What do you guys think?

In the second half of the video actions loaded before deleting the entry :confused:

Also in the first half. According to the video the actions appear while dragging :man_shrugging:.

1 Like

If that user activates Alchemist’s event listener, we might be able to see what happens when loading plugins and when dragging history.

For example, during plugin loading, we may be able to watch Photoshop trigger a modal event that should have nothing to do with the plugin at all, and then the dragging of the history will terminate it.

@sttk3 Thanks. Yep, that’s worth a try.

1 Like

I actually don’t think it’s the loading of the panel that is the point where things get bogged down. It looks like the panel has loaded just fine. The UI is clearly visible. It’s the loading of the actions, i.e. the code in your original post, that seems to be the issue.

Oh, wow, here’s an easier way to make this happen. No need to delete the “Open” history state. Just click on the original Snapshot on the History panel. That works to load the actions instantly also. In fact, clicking on any snapshot on the History panel seems to work if you have more than one snapshot. Doesn’t need to be the original.

In fact, if you have multiple history states, simply clicking on a different history state other than the current one works too. It seems that a history state change is all that’s needed.

Of course, none of this makes any sense. It almost seems like the Actions Panel and the History panel have some new cross-coding that’s making it hard to execute the @tomzag code until a new history state is selected.

1 Like

does suspending history help at all?
or triggering and undo/redo before executing your code?

How about this? https://developer.adobe.com/photoshop/uxp/2022/ps_reference/media/photoshopcore/#redrawdocument

await PhotoshopCore.redrawDocument({ documentID: 123})

So you would enforce document re-render and refresh UI and so on. But I am not sure if an action thread will be available at that time to execute this command.

I tried writing in batchPlay, but in both cases time taken was 6 msec or so for 253 actions. The numbers were too small to tell the difference in speed.

{"actions": 253, "timeTaken": 6, "os": "darwin", "osVersion": "20.6.0", "psVersion": "24.3.0"}
import photoshop from 'photoshop' ;
const { batchPlay } = photoshop.action ;

/**
  * get length of action sets
  * @return {Promise<Number>} 
*/
export const countActionSets = async () => {
  const retval = await batchPlay([
    {
      "_obj": "get", 
      "_target": [
        {
          "_property": "numberOfActionSets", 
        },
        {
          "_ref": "application", 
          "_enum": "ordinal", 
          "_value": "targetEnum", 
        }, 
      ], 
      "_options": {
        "dialogOptions": "dontDisplay", 
      }, 
    }, 
  ], 

    {}, 
  ) ;

  return retval[0].numberOfActionSets ;
} ;

/**
  * get the descriptor of an action set by index
  * @param {Number} index 1-based index of the target action set
  * @return {Promise<Object>} {name: String, itemIndex: Number, count: Number, numberOfChildren: Number, ID: Number}
*/
export const getActionSetByIndex = async (index) => {
  const retval = await batchPlay([
    {
      "_obj": "get", 
      "_target": [
        {
          "_ref": "actionSet", 
          "_index": index, 
        }, 
      ], 
    }, 
  ], 

    {}, 
  ) ;

  return retval[0] ;
} ;

/**
  * get all actions under action set
  * @param {Object} actionSetDesc descriptor of target action set
  * @return {Promise<Array>} [{name: String, ID: Number, parentName: String}...]
*/
export const getActions = async (actionSetDesc) => {
  const retval = await batchPlay([
    {
      "_obj": "multiGet", 
      "_target": {
        "_ref": [
          {
            "_ref": "actionSet", 
            "_id": actionSetDesc.ID, 
          }, 
        ], 
      }, 
      "extendedReference": [
        ["name", "ID", "parentName"], 
        {
          "_obj": "action", 
          "index": 1, 
          "count": -1, 
        }, 
      ], 
      "options": {
        "failOnMissingProperty": false, 
        "failOnMissingElement": false, 
      }, 
    }, 
  ], 

    {}, 
  ) ;

  return retval[0].list ;
} ;

/**
  * get all action sets and actions under them
  * @return {Promise<Array>} [{name: String, ID: Number, actions: Array}...]
*/
export const getAllActionSet = async () => {
  const actionSetLength = await countActionSets() ;
  const actionSets = [] ;
  for(let i = 1 ; i <= actionSetLength ; i++) {
    const currentActionSet = await getActionSetByIndex(i) ;
    const actions = await getActions(currentActionSet) ;
    const outgoingObject = {
      name: currentActionSet.name, 
      ID: currentActionSet.ID, 
      actions, 
    }
    actionSets.push(outgoingObject) ;
  }

  return actionSets ;
} ;
1 Like

does triggering and undo/redo before executing your code?

Undo / redo didn’t help in my case. Still had to actually click on the history state to trigger quick evaluation of the actionTree.

I’ve not tried that, but the problem still exists if no image is open. How do you redraw an image if there is no open image? It would still look like the panel was very slow if the actions didn’t load.

However, encasing the evaluation of the actionTree in executeAsModal appears to have worked. I can now evaluate an actionTree with 2366 actions in a fraction of a second. Hopefully it works for @tomzag too.

From @sttk3, it looks like switching to pure batchPlay works too. Sort of makes the case that batchPlay might be a safer bet over APIs, or at least it’s better to execute APIs inside executeAsModal in some cases.

1 Like