>  기사  >  웹 프론트엔드  >  JSON.stringify와 경쟁 - 사용자 정의를 구축하여

JSON.stringify와 경쟁 - 사용자 정의를 구축하여

王林
王林원래의
2024-08-14 14:35:02352검색

Competing with JSON.stringify - by building a custom one

친구와 재귀에 관해 토론하던 중에 이런 말이 나왔습니다. 왜 구축하지 않습니까
재귀 프로그래밍 연습으로 Javascript JSON.stringify 메소드를 사용하시겠습니까? 정말 좋은 것 같아요
아이디어.

첫 번째 버전의 초안을 빠르게 작성했습니다. 그리고 그것은 끔찍하게 수행되었습니다!
소요 시간은 표준 JSON.stringify의 약 4배입니다.

첫 번째 초안

function json_stringify(obj) {
  if (typeof obj == "number" || typeof obj == "boolean") {
    return String(obj);
  }

  if (typeof obj == "string") {
    return `"${obj}"`;
  }

  if (Array.isArray(obj)) {
    return "[" + obj.map(json_stringify).join(",") + "]";
  }

  if (typeof obj === "object") {
    const properties_str = Object.entries(obj)
      .map(([key, val]) => {
        return `"${key}":${json_stringify(val)}`;
      })
      .join(",");
    return "{" + properties_str + "}";
  }
}

다음을 실행하면 json_stringify가 다음과 같이 작동하는 것을 볼 수 있습니다
예상됩니다.

const { assert } = require("console");
const test_obj = {
  name: "John Doe",
  age: 23,
  hobbies: ["football", "comet study"]
};

assert(json_stringify(test_obj) === JSON.stringify(test_obj))

더 많은 시나리오를 테스트하고 여러 번 실행하여
스크립트가 실행되면서 간단한 테스트 스크립트를 만들었습니다!

간단한 테스트 스크립트

function validity_test(fn1, fn2, test_values) {
  for (const test_value of test_values) {
    assert(fn1(test_value) == fn2(test_value));
  }
}

function time(fn, num_runs = 1, ...args) {
  const start_time = Date.now()

  for (let i = 0; i < num_runs; i++) {
    fn(...args);
  }

  const end_time = Date.now()
  return end_time - start_time
}


function performance_test(counts) {
  console.log("Starting performance test with", test_obj);

  for (const count of counts) {
    console.log("Testing", count, "times");

    const duration_std_json = time(JSON.stringify.bind(JSON), count, test_obj);
    console.log("\tStd lib JSON.stringify() took", duration_std_json, "ms");

    const duration_custom_json = time(json_stringify, count, test_obj);
    console.log("\tCustom json_stringify() took", duration_custom_json, "ms");
  }
}

const test_obj = {} // a deeply nested JS object, ommitted here for brevity 
const test_values = [
  12,
  "string test",
  [12, 34, 1],
  [12, true, 1, false],
  test_obj
];

validity_test(JSON.stringify, json_stringify, test_values);
performance_test([1000, 10_000, 100_000, 1000_000]);

이를 실행하면 다음과 같은 타이밍을 얻습니다.

Testing 1000 times
    Std lib JSON.stringify() took 5 ms
    Custom json_stringify() took 20 ms
Testing 10000 times
    Std lib JSON.stringify() took 40 ms
    Custom json_stringify() took 129 ms
Testing 100000 times
    Std lib JSON.stringify() took 388 ms
    Custom json_stringify() took 1241 ms
Testing 1000000 times
    Std lib JSON.stringify() took 3823 ms
    Custom json_stringify() took 12275 ms

시스템마다 다르게 실행될 수 있지만 소요되는 시간의 비율은
표준 JSON.strngify에서 사용자 정의 json_stringify의 값은
정도여야 합니다. 1:3 - 1:4

흥미로운 경우에도 다를 수 있습니다.
에 대해 자세히 알아보려면 계속 읽어보세요. 그게!

성능 향상

가장 먼저 고칠 수 있는 점은 지도 기능의 사용입니다.
이전 배열의 새 배열. 객체의 경우
배열을 생성합니다. 객체 항목이 포함된 배열에서 JSON 문자열화된 객체 속성입니다.

배열 요소의 문자열화에서도 비슷한 일이 일어나고 있습니다.

배열의 요소나 객체의 항목을 반복해야 합니다! 하지만
JSON 문자열화된 부분을 결합하기 위해 다른 배열 생성을 건너뛸 수 있습니다.

업데이트된 버전은 다음과 같습니다. (간략화를 위해 변경된 부분만 표시)

function json_stringify(val) {
  if (typeof val === "number" || typeof val === "boolean") {
    return String(val);
  }

  if (typeof val === "string") {
    return `"${val}"`;
  }

  if (Array.isArray(val)) {
    let elements_str = "["

    let sep = ""
    for (const element of val) {
      elements_str += sep + json_stringify(element)
      sep = ","
    }
    elements_str += "]"

    return elements_str
  }

  if (typeof val === "object") {
    let properties_str = "{"

    let sep = ""
    for (const key in val) {
      properties_str += sep + `"${key}":${json_stringify(val[key])}`
      sep = ","
    }
    properties_str += "}"

    return properties_str;
  }
}

이제 테스트 스크립트의 출력은 다음과 같습니다

Testing 1000 times
        Std lib JSON.stringify() took 5 ms
        Custom json_stringify() took 6 ms
Testing 10000 times
        Std lib JSON.stringify() took 40 ms
        Custom json_stringify() took 43 ms
Testing 100000 times
        Std lib JSON.stringify() took 393 ms
        Custom json_stringify() took 405 ms
Testing 1000000 times
        Std lib JSON.stringify() took 3888 ms
        Custom json_stringify() took 3966 ms

이제 훨씬 좋아 보입니다. 우리의 사용자 정의 json_stringify는 단 3ms만 소요됩니다
깊은 중첩 객체를 10,000번 문자열화하려면 JSON.stringify보다 더 많은 것이 필요합니다.
완벽하지는 않더라도 허용되는 지연입니다.

더 짜내??

현재 지연은 모든 문자열 생성 및 연결로 인해 발생할 수 있습니다.
그런 일이 일어나고 있습니다. elements_str += sep + json_stringify(element)
를 실행할 때마다 3개의 문자열을 연결하고 있습니다.

문자열을 연결하려면 비용이 많이 듭니다

  1. 결합된 문자열 전체에 맞는 새 문자열 버퍼 생성
  2. 개별 문자열을 새로 생성된 버퍼에 복사

버퍼를 직접 사용하고 거기에 데이터를 직접 기록하면
성능 개선. 큰 버퍼(예: 80자)를 만들 수 있기 때문에
그런 다음 부족하면 80자를 더 수용할 수 있도록 새 버퍼를 만듭니다.

데이터 재할당/복사를 완전히 피하지는 않겠지만,
이러한 작업을 줄입니다.

또 다른 지연 가능성은 재귀 프로세스 자체입니다! 특히
시간이 걸리는 함수 호출. 함수 호출 json_stringify(val)
을 고려해 보세요. 매개변수가 하나뿐입니다.

함수 호출 이해

단계는 다음과 같습니다

  1. 반환 주소를 스택에 푸시
  2. 인수 참조를 스택에 푸시
  3. 호출된 함수에서
    1. 스택에서 매개변수 참조를 팝합니다
    2. 스택에서 반환 주소를 팝합니다
    3. 반환 값(문자열화된 부분)을 스택에 푸시
  4. 호출 기능에서
    1. 스택에서 함수가 반환한 값을 꺼냅니다

이러한 모든 작업은 함수 호출이 발생하도록 하기 위해 발생하며 이로 인해 CPU가 추가됩니다
비용이 듭니다.

json_stringify의 비재귀 알고리즘을 생성하면 이러한 모든 작업이 수행됩니다
위에 나열된 함수 호출(해당 호출 횟수의 곱)은 다음과 같습니다.
없음으로 줄였습니다.

이는 향후 시도가 될 수 있습니다.

NodeJs 버전 차이

여기서 마지막으로 주의할 점은 다음과 같습니다. 테스트 스크립트의 다음 출력을 고려하세요

Testing 1000 times
        Std lib JSON.stringify() took 8 ms
        Custom json_stringify() took 8 ms
Testing 10000 times
        Std lib JSON.stringify() took 64 ms
        Custom json_stringify() took 51 ms
Testing 100000 times
        Std lib JSON.stringify() took 636 ms
        Custom json_stringify() took 467 ms
Testing 1000000 times
        Std lib JSON.stringify() took 6282 ms
        Custom json_stringify() took 4526 ms

우리의 사용자 정의 json_stringify가 NodeJs 표준보다 성능이 더 좋았나요
JSON.stringify???

그렇습니다! 하지만 이는 NodeJs의 이전 버전(v18.20.3)입니다.
이 버전(그리고 아마도 더 낮은 버전)은 우리가 만든 json_stringify가 작동합니다
표준 라이브러리보다 빠릅니다!

이 기사에 대한 모든 테스트(마지막 기사 제외)는
로 완료되었습니다. 노드 v22.6.0

JSON.stringify의 성능이 v18에서 v22로 향상되었습니다. 너무 좋아요

또한 우리 스크립트가 NodeJs v22에서 더 나은 성능을 발휘했다는 점도 중요합니다.
즉, NodeJs가 런타임의 전반적인 성능도 향상했다는 의미입니다.
아마도 기본 V8 엔진 자체에 업데이트가 발생했을 수 있습니다.

글쎄, 이것은 나에게 즐거운 경험이었습니다. 그리고 그것이
너도. 그리고 이 모든 즐거움 속에서 우리는 한두 가지를 배웠습니다!

계속 구축하고 계속 테스트하세요!

위 내용은 JSON.stringify와 경쟁 - 사용자 정의를 구축하여의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.