Improve escapeLiteral performance of long strings by 30x by Llorx · Pull Request #3553 · brianc/node-postgres

if (backslash) {
    const pos = i % size;
    str[pos] = "\\";
}
if (quote) {
    const pos = (i + 2) % size;
    str[pos] = "'";
}

str is a string (immutable), so this benchmark doesn’t actually test any quotes or backslashes.

Ah my bad. I was working with buffers and failed miserably when doing the benchmark haha. Here is the fixed benchmark:
image

Backslash penalizes way more than quote because of the callback to avoid another iteration to find a backslash. Still a great performance improvement, specially when there's nothing to clean (which is the usual case).

The code:

const { IsoBench } = require("iso-bench");

function escapeLiteralOriginal(str) {
  let hasBackslash = false
  let escaped = "'"

  if (str == null) {
    return "''"
  }

  if (typeof str !== 'string') {
    return "''"
  }

  for (let i = 0; i < str.length; i++) {
    const c = str[i]
    if (c === "'") {
      escaped += c + c
    } else if (c === '\\') {
      escaped += c + c
      hasBackslash = true
    } else {
      escaped += c
    }
  }

  escaped += "'"

  if (hasBackslash === true) {
    escaped = ' E' + escaped
  }

  return escaped
}
const escapeLiteralNew = function (str) {
  if (typeof str !== 'string') {
    return "''"
  }
  let hasBackslash = false
  if (str.length < 13) {
    let escaped = "'"

    for (let i = 0; i < str.length; i++) {
      const c = str[i]
      if (c === "'") {
        escaped += c + c
      } else if (c === '\\') {
        escaped += c + c
        hasBackslash = true
      } else {
        escaped += c
      }
    }
  
    escaped += "'"
  
    if (hasBackslash === true) {
      escaped = ' E' + escaped
    }
    return escaped
  } else {
    let escaped = str
        .replace(/\\/g, () => {
            hasBackslash = true
            return '\\\\'
        })
        .replace(/'/g, "''")
    
    if (hasBackslash) {
        escaped = ` E'${escaped}'`
    } else {
        escaped = `'${escaped}'`
    }
    return escaped
  }
}
function newArray(size, backslash, quote, count = 100) {
    return new Array(count).fill(0).map((_, i) => {
        let str = "a".repeat(size);
        if (backslash) {
            const pos = i % size;
            str = `${str.substring(0, pos)}\\${str.substring(pos)}`;
        }
        if (quote) {
            const pos = (i + 2) % size;
            str = `${str.substring(0, pos)}'${str.substring(pos)}`;
        }
        return str;
    });
}
new IsoBench()
    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(10, false, false))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(10, false, false))
.endGroup("short string, backslash = false, quote = false")

    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(10, true, false))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(10, true, false))
.endGroup("short string, backslash = true, quote = false")

    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(10, false, true))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(10, false, true))
.endGroup("short string, backslash = false, quote = true")

    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(10, true, true))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(10, true, true))
.endGroup("short string, backslash = true, quote = true")

    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(1000, false, false))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(1000, false, false))
.endGroup("long string, backslash = false, quote = false")

    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(1000, true, false))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(1000, true, false))
.endGroup("long string, backslash = true, quote = false")

    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(1000, false, true))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(1000, false, true))
.endGroup("long string, backslash = false, quote = true")

    .add("escapeLiteralNew", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralNew(arr[i]);
        }
    }, () => newArray(1000, true, true))
    .add("escapeLiteralOriginal", (arr) => {
        for (let i = 0; i < arr.length; i++) {
            escapeLiteralOriginal(arr[i]);
        }
    }, () => newArray(1000, true, true))
.endGroup("long string, backslash = true, quote = true")
    .consoleLog()
    .run();