Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(step-function): optimizer #425

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open

feat(step-function): optimizer #425

wants to merge 16 commits into from

Conversation

thantos
Copy link
Collaborator

@thantos thantos commented Aug 19, 2022

  • Attempt to reduce unnecessary variables in a ASL state machine.
  • Add options to turn on or off optimizations

@netlify
Copy link

netlify bot commented Aug 19, 2022

Deploy Preview for effortless-malabi-1c3e77 ready!

Name Link
🔨 Latest commit d917cb4
🔍 Latest deploy log https://app.netlify.com/sites/effortless-malabi-1c3e77/deploys/6327d22d0933b60008d9f468
😎 Deploy Preview https://deploy-preview-425--effortless-malabi-1c3e77.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@thantos thantos self-assigned this Aug 19, 2022
Copy link
Collaborator

@sam-goodwin sam-goodwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing change. Great job mr compiler man.

I'm not all the way through but here is some initial feedback.

Comment on lines +412 to +426
export function normalizeJsonPath(jsonPath: string): (string | number)[] {
return (
jsonPath.match(/(\.[a-zA-Z0-9_\$]*|\[\'?[^'\]]*\'?\])/g)?.map((seg) => {
if (seg.startsWith(".")) {
return seg.substring(1);
} else if (seg.startsWith("['")) {
return seg.substring(2, seg.length - 2);
} else if (seg.startsWith("[")) {
return Number(seg.substring(1, seg.length - 1));
} else {
throw new Error("Invalid json path: " + jsonPath);
}
}) ?? []
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This scares me. Can you document the assumptions that go into this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a json path ast we could use instead? Parse the string into its AST and process that rather than string matching?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, was also worried about this, will try: https://github.com/dchester/jsonpath#readme

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a spike on this and found the results disappointing, current state works for now.

https://twitter.com/sussmansa/status/1562523469094330369

src/util.ts Show resolved Hide resolved
src/util.ts Outdated Show resolved Hide resolved
Comment on lines +513 to +526
test.each([
["with", undefined],
[
"without",
{
optimization: {
joinConsecutiveChoices: false,
optimizeVariableAssignments: false,
removeNoOpStates: false,
removeUnreachableStates: false,
},
} as ASLOptions,
],
])("%s optimizer", (_, opt) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this test.

Does this mean that all optimizations are turned on by default?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. For now at least.

  export const DefaultOptimizeOptions: OptimizeOptions = {
    optimizeVariableAssignments: true,
    removeUnreachableStates: true,
    joinConsecutiveChoices: true,
    removeNoOpStates: true,
  };

I imagine we'll create new, experimental ones or versions of existing ones and make a breaking change to turn them on.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this test.

I am glad I wrote it, found that options in SFN were borked this whole time 🤒

Comment on lines +4135 to +4137
// TODO: https://github.com/functionless/functionless/issues/420
// https://mathiasbynens.be/notes/javascript-properties
test.skip("unicode variable names", () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is so awesome. Shame we skip it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just need to go the ticket to make sure it is all supported. Mostly need to normalize to valid characters in ASL.

src/asl.ts Outdated Show resolved Hide resolved
src/asl.ts Outdated
Comment on lines 5908 to 5929
* When {@link Choice} states are chained together, they can be simplified by merging the branches together.
*
* ```ts
* if(a) {
* if(b) {
* } else {
* }
* }
*
* =>
*
* ```ts
* if(a && b) {}
* else(a) {}
* ```
*
* In ASL this may remove unnecessary state transitions without any impact on the logic.
*
* Note: This could make the ASL itself larger when the same Choice is merged into multiple calling choices.
* Turn off at the cost of adding more state transitions.
*/
joinConsecutiveChoices: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it!

Do we have a test case where there are subsequent choices except the inner one has an else?

if(a) {
  if(b) {}
  else {}
}

Copy link
Collaborator Author

@thantos thantos Aug 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this was in there before)

We did not, added one

      if (input.val !== "a") {
        if (input.val === "b") {
          return "hullo";
        } else {
          return "wat";
        }
      }
      return "woop";
    "if(input.val !== \"a\")": Object {
      "Choices": Array [
        Object {
          "And": Array [
            Object {
              "Not": Object {
                "And": Array [
                  Object {
                    "IsPresent": true,
                    "Variable": "$.input.val",
                  },
                  Object {
                    "And": Array [
                      Object {
                        "IsString": true,
                        "Variable": "$.input.val",
                      },
                      Object {
                        "StringEquals": "a",
                        "Variable": "$.input.val",
                      },
                    ],
                  },
                ],
              },
            },
            Object {
              "And": Array [
                Object {
                  "IsPresent": true,
                  "Variable": "$.input.val",
                },
                Object {
                  "And": Array [
                    Object {
                      "IsString": true,
                      "Variable": "$.input.val",
                    },
                    Object {
                      "StringEquals": "b",
                      "Variable": "$.input.val",
                    },
                  ],
                },
              ],
            },
          ],
          "Next": "return \"hullo\"",
        },
        Object {
          "Next": "return \"wat\"",
          "Not": Object {
            "And": Array [
              Object {
                "IsPresent": true,
                "Variable": "$.input.val",
              },
              Object {
                "And": Array [
                  Object {
                    "IsString": true,
                    "Variable": "$.input.val",
                  },
                  Object {
                    "StringEquals": "a",
                    "Variable": "$.input.val",
                  },
                ],
              },
            ],
          },
        },
      ],
      "Default": "return \"woop\"",
      "Type": "Choice",
    },

src/asl.ts Outdated
*/
joinConsecutiveChoices: boolean;
/**
* The Functionless transpiler and sometimes user can can create states that don't do anything.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Examples?

src/asl.ts Outdated
}

/**
* Run optimizations that reduce the number of states in the graph
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that all of them?

src/asl.ts Outdated
}

/**
* For now, all optimizations only support a single usage of the current variable.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an issue tracking the outstanding tasks?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants