Skip to content

Faster Transform stream #33397

Closed
Closed
@ronag

Description

@ronag

Some thoughts on how to make Transform streams faster.

Part of the overhead of Transform (and PassThrough) is that it is actually 2 streams, one Writable and one Readable, both with buffering and state management, which are connected together.

We could try to skip this and implement Transform as a Readable which implements the Writable interface and proxies the naming.

e.g.

class FastTransform extends Readable {
  constructor(options) {
    super(options)
    this._writableState = {
      length: 0,
      needDrain: false,
      ended: false,
      finished: false
    }
  }
  get writableEnded () {
    return this._writableState.ended
  }
  get writableFinished () {
    return this._writableState.finished
  }
  _read () {
    const rState = this._readableState
    const wState = this._writableState

    if (!wState.needDrain) {
      return
    }

    if (wState.length + rState.length > rState.highWaterMark) {
      return
    }

    wState.needDrain = false
    this.emit('drain')
  }
  write (chunk) {
    const rState = this._readableState
    const wState = this._writableState

    const len = chunk.length

    wState.length += len
    this._transform(chunk, null, (err, data) => {
      wState.length -= len
      if (err) {
        this.destroy(err)
      } else if (data != null) {
        this.push(data)
      }
      this._read()
    })

    wState.needDrain = wState.length + rState.length > rState.highWaterMark

    return wState.needDrain
  }
  end () {
    const wState = this._writableState

    wState.ended = true
    if (this._flush) {
      this._flush(chunk, (err, data) => {
        const wState = this._writableState
        if (err) {
          this.destroy(err)
        } else {
          if (data != null) {
            this.push(data)
          }
          this.push(null)
          wState.finished = true
          this.emit('finish')
        }
      })
    } else {
      this.push(null)
      wState.finished = true
      this.emit('finish')
    }
  }
}

// TODO: Make Writable[Symbol.hasInstance] recognize `FastTransform`.

Making this fully backwards compatible with Transform might be difficult.

Metadata

Metadata

Assignees

Labels

streamIssues and PRs related to the stream subsystem.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions