Skip to content

add support for inlining methods#49

Merged
jdalton merged 1 commit into
nirgit:masterfrom
jdalton:jdalton/inline
May 10, 2024
Merged

add support for inlining methods#49
jdalton merged 1 commit into
nirgit:masterfrom
jdalton:jdalton/inline

Conversation

@jdalton

@jdalton jdalton commented Apr 30, 2024

Copy link
Copy Markdown
Collaborator

Add support for inlining methods.

Intended after #48.

@nirgit nirgit left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

thanks @jdalton

could you give some detailed explanation on the work done on the rollup.config.js part?
i don't understand what you are trying to achieve and if the code complexity tradeoff is worth it
(assuming this is done for perf optimization, it'd be helpful to see some test measurements of "before" vs "after")

Comment thread src/constants.js
// Each constant is inlined by Rollup.
module.exports = {
BUILD: {
COMPTIME: true

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

explain?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Languages like Zig and others have a notion of at "compile time". So when we are building the lib output with rollup and we load the modules it is at "compile time" this way during compile time we can export things that need to be inlined else not export them. The rollup "replace" plugin takes care of it seamlessly.

Comment thread src/model.js
Comment thread src/constants.js Outdated
Comment thread rollup.config.js Outdated
@jdalton jdalton force-pushed the jdalton/inline branch 4 times, most recently from 1a6a831 to fe2d6ce Compare May 3, 2024 20:22
Comment thread rollup.config.js
],
plugins: [commonjs()]
};
}

@jdalton jdalton May 3, 2024

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I moved the "inline" stuff to its own module. It's designed in a way that you can not inline and all the code still works. Inlining just allows code to be annotated like peek(/*inline*/) and it will inline the code at compile time but keep it abstracted during development.

You can see this here:

if (code === CHAR_CODE.OPEN_BRACKET) {
pos += 1
if (
hasNext(/*inline*/) &&
peek(/*inline*/) === CHAR_CODE.FORWARD_SLASH
) {

when inlined it will become:

if (code === 60) {
  pos += 1
  if (
      (currentToken !== EOF_TOKEN && pos < length) &&
      (xmlAsString.charCodeAt(pos)) === 47
  ) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

JavaScript engines perform inlining during their JIT phase. However because parsing/tokenizing/creating ast can be intensive having it inlined upfront can aid JS engines in their JIT because more things are known up-front (less calling out to other functions).

@jdalton jdalton May 3, 2024

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

With inlining enabled in PR #47 the performance is:

After for 4,000 iterations:

avg exec time of 4000 iterations (in ms): 0.553
...
Time: 2.459 s, estimated 3 s

without inlining (so removing --inline from the run command) it is:

After for 4,000 iterations:

avg exec time of 4000 iterations (in ms): 0.743
...
Time: 3.206 s

The idea is that inlining is bonus and so that's why it's an easy on/off and code works without inlining. The inlining bit is an extra oomph that does produce consistently faster results.

Comment thread package.json
@jdalton jdalton force-pushed the jdalton/inline branch 2 times, most recently from 07ab8c1 to 11d5615 Compare May 5, 2024 11:12
@jdalton

jdalton commented May 5, 2024

Copy link
Copy Markdown
Collaborator Author

Updated to simplify the inline bits by using acorn to reduce reliance on regexp.

@jdalton jdalton force-pushed the jdalton/inline branch 4 times, most recently from e175952 to df16f60 Compare May 6, 2024 18:16
@nirgit

nirgit commented May 7, 2024

Copy link
Copy Markdown
Owner

cool stuff @jdalton sorry for the delay reviewing this, i'll try to get to this by the end of the week

any chance you're adding some unit tests to the inline plugin you wrote until then?
thanks!

@jdalton

jdalton commented May 8, 2024

Copy link
Copy Markdown
Collaborator Author

@nirgit

any chance you're adding some unit tests to the inline plugin you wrote until then? thanks!

At the moment the plugin will warn if it misses an inline (you specify inlining something it doesn't have the source for) and will also throw an error for unsupported syntax (complete with snippet of code at the line and the a "^" under the identifier in question.

@nirgit nirgit left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

since the tests pass, and it's possible to disable the inline plugin easily (thanks!) and since it's contained, i'm fine with approving this PR, although there are 2 things that are still bugging me:

  1. i think that the inline-plugin.js should have a dedicated unit test, as this module is not trivial to understand, and will stay hard to maintain in the future (making changes with less confidence).
  2. it is unclear to me how you tested the performance before and after. can you please be very detailed about it?
    fwiw, what i did was build master, then using another testing node module that imports it
// package.json for testing module
...
"dependencies": {
    "simple-xml-to-json": "file:../simple-xml-to-json"
  }
// the testing script
'use strict'

const fs = require('fs')
const {convertXML} = require("simple-xml-to-json/lib/simpleXmlToJson.min.js")
// const {convertXML} = require("simple-xml-to-json")

const fileName = 'mock2.xml'
const fileToParse = fs.readFileSync(__dirname + "/" + fileName, {encoding: 'utf8'})

const start = Date.now()
const loops = 5000
for (let i = 0; i < loops; i++) {
    convertXML(fileToParse)
}
const end = Date.now()
console.log('average time for converting XML = ' + ((end-start)/loops) + 'ms')

surprisingly, when i did the same on your branch, i didn't see any difference in performance, so i'm not sure what i'm missing :\

thanks @jdalton for your contribution and patience 🙏🏻

🌿

Comment thread package.json Outdated
Comment thread package.json
Comment thread scripts/inline-plugins.js
}
},
replacePlugin({
preventAssignment: false,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

shouldn't this be true?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Making it true prevents replacement in case: statements in switchs. As long as we aren't assigning to the constants this is fine to be true.

Comment thread scripts/inline-plugins.js
}
})()

const inlinableCallExpressionReplacer = (match, funcName, args) => {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

would it make more sense to rename match to source?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

In this case its simulating string replace where a pattern is matched and then capture groups like funcName and args follow.

@jdalton

jdalton commented May 10, 2024

Copy link
Copy Markdown
Collaborator Author

@nirgit

surprisingly, when i did the same on your branch, i didn't see any difference in performance, so i'm not sure what i'm missing :\

This branch is just adding the inline support but not yet taking advantage of it. That happens in the performance PR.

@jdalton jdalton merged commit ec2e488 into nirgit:master May 10, 2024
@jdalton jdalton deleted the jdalton/inline branch May 10, 2024 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants