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

Enhancement: Optimize time scale #136

Merged
merged 3 commits into from
May 2, 2023
Merged

Enhancement: Optimize time scale #136

merged 3 commits into from
May 2, 2023

Conversation

pleek91
Copy link
Collaborator

@pleek91 pleek91 commented May 1, 2023

Description

The logic for determining the minimum and maximum dates for the data looped over every node in the dataset 3 times. Refactoring the logic to only loop once produces a 78% performance improvement in the benchmark I ran.

@brandonreid there was a bit of logic around isRunning which I ended up leaving out. Not because it was causing any problems, but because it didn't seem to effect any of the tests I did. Thoughts on that? I'd be fine with adding that back in just to be safe. But maybe you know why that would be needed.

Benchmark performed with https://jsbench.me/
image

Setup

const data = [
  {
    "id": "66d76a19-ffb2-4966-b84a-4c5e39b673f6",
    "upstreamDependencies": [],
    "state": "completed",
    "label": "omicron6-landau",
    "start": new Date("2023-05-01T14:23:20.117Z"),
    "end": new Date("2023-05-01T14:24:00.110Z"),
  },
  {
    "id": "a2a6708a-6799-4304-8466-fe2800991928",
    "upstreamDependencies": [
      "66d76a19-ffb2-4966-b84a-4c5e39b673f6"
    ],
    "state": "failed",
    "label": "pi57-ikeda-mass",
    "start": new Date("2023-05-01T14:24:00.110Z"),
    "end": new Date("2023-05-01T14:24:39.805Z"),
    "subFlowRunId": "9a3be9a1-e097-415c-a75f-1238653f8873"
  },
  {
    "id": "1969e424-64d2-41c2-b61e-1b8bcd312729",
    "upstreamDependencies": [
      "66d76a19-ffb2-4966-b84a-4c5e39b673f6"
    ],
    "state": "paused",
    "label": "omega2-ornara",
    "start": new Date("2023-05-01T14:24:00.110Z"),
    "end": new Date("2023-05-01T14:24:44.805Z"),
    "subFlowRunId": "f5ab9e98-aaf5-4b2d-9faf-7ad40a3e6d7a"
  },
  {
    "id": "9bc097c8-92eb-4fe2-bb25-b164e197d67e",
    "upstreamDependencies": [
      "1969e424-64d2-41c2-b61e-1b8bcd312729",
      "a2a6708a-6799-4304-8466-fe2800991928",
      "a2a6708a-6799-4304-8466-fe2800991928"
    ],
    "state": "crashed",
    "label": "mu50-comae-berenices-g",
    "start": new Date("2023-05-01T14:24:44.805Z"),
    "end": new Date("2023-05-01T14:25:11.903Z"),
    "subFlowRunId": "f65354e6-6362-4348-b1d8-8ef8726da43c"
  },
  {
    "id": "0616bede-e293-45dc-9e09-4dc5d36879a7",
    "upstreamDependencies": [
      "1969e424-64d2-41c2-b61e-1b8bcd312729",
      "a2a6708a-6799-4304-8466-fe2800991928"
    ],
    "state": "completed",
    "label": "xi4-anfalas-anomaly",
    "start": new Date("2023-05-01T14:24:44.805Z"),
    "end": new Date("2023-05-01T14:25:17.726Z")
  },
  {
    "id": "b7af3821-6683-4892-b3d7-41afce71244d",
    "upstreamDependencies": [
      "1969e424-64d2-41c2-b61e-1b8bcd312729"
    ],
    "state": "crashed",
    "label": "delta2-muneca",
    "start": new Date("2023-05-01T14:24:44.805Z"),
    "end": new Date("2023-05-01T14:25:28.386Z"),
    "subFlowRunId": "99c71553-821a-42ed-bd11-a7b0564b2710"
  },
  {
    "id": "c92b048f-9fdc-4a1c-9638-749b4cda1070",
    "upstreamDependencies": [
      "b7af3821-6683-4892-b3d7-41afce71244d"
    ],
    "state": "crashed",
    "label": "chi887-elba",
    "start": new Date("2023-05-01T14:25:28.386Z"),
    "end": new Date("2023-05-01T14:26:05.712Z")
  },
  {
    "id": "dce9235f-d445-4c26-a467-29c9b617a143",
    "upstreamDependencies": [
      "b7af3821-6683-4892-b3d7-41afce71244d"
    ],
    "state": "pending",
    "label": "eta6-geminorum-p",
    "start": new Date("2023-05-01T14:25:28.386Z"),
    "end": new Date("2023-05-01T14:26:00.069Z")
  },
  {
    "id": "508f4d50-59dd-452f-9318-87eb8a50daa1",
    "upstreamDependencies": [
      "b7af3821-6683-4892-b3d7-41afce71244d"
    ],
    "state": "crashed",
    "label": "pi681-becan-f",
    "start": new Date("2023-05-01T14:25:28.386Z"),
    "end": new Date("2023-05-01T14:25:52.585Z"),
    "subFlowRunId": "4b5c0809-e077-431e-a515-915209e35662"
  },
  {
    "id": "c3a7adfa-1cb3-4961-8ee4-dfd59e06a39d",
    "upstreamDependencies": [
      "0616bede-e293-45dc-9e09-4dc5d36879a7",
      "9bc097c8-92eb-4fe2-bb25-b164e197d67e",
      "9bc097c8-92eb-4fe2-bb25-b164e197d67e"
    ],
    "state": "cancelled",
    "label": "omega-sirrie",
    "start": new Date("2023-05-01T14:25:17.726Z"),
    "end": new Date("2023-05-01T14:25:41.744Z")
  }
]

New test case

function initTimeScaleProps(graphData) {
  const minimumTimeSpan = 1000 * 60
  const { min, max, span } = getDateBounds(graphData, minimumTimeSpan)
}

function getDateBounds(data, minimumTimeSpan = 0) {
  const [minStartTime, maxStartTime] = data.reduce(([min, max], { start, end }) => {
    const startTime = start?.getTime() ?? null
    const endTime = end?.getTime() ?? null

    if (startTime && min) {
      min = Math.min(min, startTime)
    }

    if (endTime && max) {
      max = Math.max(max, endTime)
    }

    return [min ?? startTime, max ?? endTime]
  }, [null, null])

  const min = minStartTime ? new Date(minStartTime) : new Date()
  let max = maxStartTime ? new Date(maxStartTime) : new Date()
  let span = max.getTime() - min.getTime()

  if (span < minimumTimeSpan) {
    max = new Date(min.getTime() + minimumTimeSpan)
    span = minimumTimeSpan
  }

  return {
    min,
    max,
    span,
  }
}

initTimeScaleProps(data)

Old test case

function hasStartAndEndDates(node) {
  return !!node.start && !!node.end
}

function initTimeScaleProps(graphData) {
  const minimumTimeSpan = 1000 * 60

  const dates = graphData
    .filter(hasStartAndEndDates)
    .map(({ start, end }) => ({ start, end }))


  const { min, max, span } = getDateBounds(dates, minimumTimeSpan)
}

function getDateBounds(datesArray, minimumTimeSpan) {
  let min
  let max

  datesArray.forEach((dates) => {
    if (
      min === undefined
        || min > dates.start
        || isNaN(dates.start.getDate())
    ) {
      min = dates.start
    }

    if (
      dates.end !== null
      && (
        max === undefined
        || max < dates.end
        || isNaN(dates.end.getDate())
      )
    ) {
      max = dates.end
    }
  })

  min = min ?? new Date()
  max = max ?? new Date(min.getTime() + (minimumTimeSpan ?? 0))
  const timeSpan = max.getTime() - min.getTime()

  return {
    min,
    max,
    span: minimumTimeSpan && timeSpan < minimumTimeSpan ? minimumTimeSpan : timeSpan,
  }
}

initTimeScaleProps(data)

@netlify
Copy link

netlify bot commented May 1, 2023

Deploy Preview for prefect-graphs ready!

Name Link
🔨 Latest commit 721c2e4
🔍 Latest deploy log https://app.netlify.com/sites/prefect-graphs/deploys/64511c6d5fe9ca0007e25180
😎 Deploy Preview https://deploy-preview-136--prefect-graphs.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.

Copy link
Contributor

@brandonreid brandonreid left a comment

Choose a reason for hiding this comment

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

So I think isRunning will still need to be accounted for somehow, but this is a very welcomed and big improvement!

src/FlowRunTimeline.vue Show resolved Hide resolved
@pleek91 pleek91 merged commit 5316659 into main May 2, 2023
@pleek91 pleek91 deleted the optimize-time-scale branch May 2, 2023 14:25
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