@acromedia/sloth 中文文档教程

发布于 3年前 浏览 16 项目主页 更新于 3年前

Description

Sloth 是一个 Node 模块,旨在允许在 NodeJS 脚本、项目和测试中进行简单和通用的内存分析。

Table of Contents

Installation

  • With npm:
  npm install sloth
  • With yarn:
  yarn add sloth

Usage

ES6 imports:

import * as sloth from 'sloth'

// OR

import { Profiler, bench } from 'sloth'

使用 requires:

const sloth = require('sloth')

// OR

const { Profiler, bench, benchFile } = require('sloth')

有关更详细的描述和代码示例,请参见下文


Documentation

Using the Profiler class

:分析任何应用程序的方法,只要你有它的 PID,尽管它的目的更多是分析它在其中创建的应用程序。

Creating an instance

const { Profiler } = require('sloth')
const profiler = new Profiler(pid, options)

选项在下面描述,所有都是可选的:

OptionTypeDescription
toFileBoolean (optional)Whether to export the results to a file or not
timestepNumber (optional)How long, in milliseconds, the memory monitor will check memory usage
waitNumber (optional)How long, in milliseconds, the profiler should wait before returning results
trimNodeProcessUsageBoolean (optional)Takes the memory usage of the node process before anything has happened, and removes that from the data. Should allow for yielding more accurate results in most cases

方法在下面描述,都是异步的:

MethodDescription
startBegins the profiling, returns itself
endStops profiling, returns instance of ProfileResults. See more about ProfileResults here

Examples

创建一个新的 Profiler 实例,它将保持跟踪它自己的进程的使用情况:

const { Profiler } = require('sloth')
const profiler = new Profiler(process.pid, {
  timestep: 100,
  wait: 1000,
  // Helps since the profiler will likely skew the results otherwise
  trimNodeProcessUsage: true
})

分析大型数组的创建:

await profiler.start()

// 100,000,000 zeros
let myHugeArr = new Array(1e8).fill(0)

const results = (await profiler.end()).results
console.log(results)

在 Jest 中使用 Profiler

const { Profiler } = require('sloth')

// Your Jest test suite
describe('test test', () => {
  // To make the tests cleaner, you should probably make them async
  it ('does something', async () => {
    const profiler = new Profiler(process.pid, {
      timestep: 100,
      wait: 1000,
      // This will shave off all of the memory currently
      // taken up by the Jest test. This way, it'll be more
      // accurate in case you just did a bunch of crazy
      // stuff before and didn't clean it all up properly.
      trimNodeProcessUsage: true
    })

    await profiler.start()

    // 100,000,000 zeros
    let myHugeArr = new Array(1e8).fill(0)

    const results = (await profiler.end()).results

    // Should've taken less than 5 seconds
    // (I doubt it'd actually take that little time, but it's just an example)
    expect(results.time_elapsed < 5000).toBeTruthy()
  })
})

Extra Notes

根据所分析的功能有多大,您可能希望保留 节点垃圾收集器 记在心里。 考虑以下示例:

await profiler.start()

// 100,000,000 zeros
let myHugeArr = new Array(1e8).fill(0)
myHugeArr = null

const results = (await profiler.end()).results
console.log(results)

当数组设置为 null 时,除非运行垃圾收集器,否则内存实际上不会更改。 要更改此设置,请使用 --expose-gc 标志运行脚本,如下所示:

node --expose-gc index

并调用垃圾收集器:

await profiler.start()

// 100,000,000 zeros
let myHugeArr = new Array(1e8).fill(0)
myHugeArr = null

// Important:
global.gc()

const results = (await profiler.end()).results
console.log(results)

Using the bench function

bench() 函数接受一个函数,将其抛出进入一个单独的进程,在进程上运行分析器,并将它们全部包装在一起,并在顶部打上蝴蝶结*。

* 免责声明:实际上并没有在顶部提供蝴蝶结。

Calling bench()

调用 bench() 函数是这样完成的:

const { bench } = require('sloth')
await bench(function, arguments, options)

它将返回一个 ProfileResults 实例(有关 ProfileResults 的更多信息,请点击此处)。

有关选项的详细信息,请参阅使用 Profiler 类,因为此函数使用完全相同的选项,但有一个重要的补充:

OptionTypeDescription
setupFunction (optional)Code to run in the "global" scope. Useful for require()s and other otherwise globally defined variables
requirementsArray (optional)A nicer alternative to setup. Each item should be an object with a name (what it's defined as) and path (what is actually requireed)

Examples

* 仅供参考:“全局”一词" 通常在引号中,因为它在技术上不是全局的,而是在函数的外部范围内。

设置测量大型数组创建的测试:

function f() {
  let arr = new Array(1e8).fill(0)
}

// OR

const f = () => {
  let arr = new Array(1e8).fill(0)
}

const results = await bench(f, [], {
  // Less useful, since the process is basically isolated, but still a good idea
  trimNodeProcessUsage: true
})

测试匿名函数:

const results = bench(() => {
  console.log('I work!')
})

// OR

const results = bench(function() {
  console.log('I work!')
})

使用参数:

function log(text) {
  console.log(text)
}

const results = bench(log, ['I work!'])

// OR 

const results = bench((text) => console.log(text), ['I work!'])

使用设置函数:

// Logging a "globally" defined variable
function f() {
  console.log(myVar)
}

const results = await bench(f, [], {
  setup: () => {
    let myVar = 'I work!'
  }
})
// Using a module imported using require()
function f(data) {
  fs.writeFileSync('text.txt', data, 'utf8')
}

const results = await bench(f, ['I work!'], {
  setup: () => {
    const fs = require('fs')
  }
})

* Linters 可能会对您的设置函数非常生气,因为它们定义了未使用的变量。 只是提醒一下。

使用需求数组

// Using a package
function f() {
  filesystem.writeFileSync('test.txt', 'test')
}

const results = await bench(f, [], {
  requirements: [
    {
      // Parsed as `const filesystem = require('fs')`
      name: 'filesystem',
      path: 'fs'
    }
  ]
})

Extra Notes

当一个线程被生成时,它会自动使用 --expose-gc 选项运行,并且一旦一切都完成,它将始终运行 global.gc() ,但在探查器完成之前。 这是为了更好地了解结束内存使用情况(results 对象中的 end_usage_bytes。)。

显然,以序列化到反序列化的方式运行代码时存在安全隐患,即使它在单独的进程中并且您可以完全控制进入的代码。注意如何以及在何处使用 bench(),有时使用 Profiler 类会更安全。

Benchmarking a file

benchFile() 函数采用现有的 bench() 函数及其所有有趣的函数包装优点,并为整个文件实现它。 它允许您输入路径以及 Node AND CLI 选项,这应该允许您控制究竟运行什么。 这也会返回 ProfileResults 的一个实例。

它采用三个参数:

OptionTypeDescription
pathStringThe path to the file. This can be relative or absolute, although absolute is much more ideal.
nodeArgsArray (optional)An array of Node options, like --expose-gc. These are options passed to the Node process itself
cliArgsArray (optional)An array of CLI options. These are options likely found or used in your own code.

Examples

对单个文件

const { benchFile } = require('sloth')

await benchFile('/home/project/myFile.js')

进行基准测试 对文件进行基准测试并提供一些 Node 参数

await benchFile(__dirname + '/myFile.js/', [
  '--expose-gc',
  '--no-warnings'
])

对文件进行基准测试并提供一些 CLI 参数

await benchFile(__dirname + '/myFile.js', [], [
  '--do-ten-times',
  '-f',
  '--silent',
  '--input-dir=' + getSomeDir()
])

ProfileResults

一旦 Profiler.end()bench() 是调用,它将返回一个 ProfileResults 实例。 这包含所有分析数据,以及一些有助于查看和理解数据的额外功能。

ProfileResults.data 属性中的值概述如下:

PropertyTypeDescription
startNumberThe timestamp in milliseconds the profiling was started
endNumberThe timestamp in milliseconds the profiling finished
time_elapsedNumberThe amount of time profiling took, in milliseconds
timestep_msNumberThe amount of milliseconds per memory check
mem_listArrayList of memory values collected
startusagebytesNumberThe amount of bytes being used before or as profiling began
peakusagebytesNumberThe largest amount of memory being used at one time
endusagebytesNumberThe amount of memory being used by the end of the profile
baseprocessbytesNumberThe amount of memory used by the process without anything having been done

提供的一些方法应该有助于理解某些数据:

MethodDescription
averageMemoryUsage()Get average memory usage throughout the whole profile
medianMemoryUsage()Get middle memory usage, when list is sorted least to greatest or greatest to least
modeMemoryUsage()Get most frequently occuring memory value
memoryAtElapsed(ms)Get the amount of memory being used at a certain point in the profile

Extra Notes

如果您打算在自动化测试中使用此模块,请记住该分析在不同的机器上可能需要不同的时间。 这可能会扭曲平均值等,因此请坚持测试更具体的值,例如 peak_usage_bytes

Description

Sloth is a Node module created with the intention of allowing easy and versatile memory profiling in NodeJS scripts, projects, and tests.

Table of Contents

Installation

  • With npm:
  npm install sloth
  • With yarn:
  yarn add sloth

Usage

ES6 imports:

import * as sloth from 'sloth'

// OR

import { Profiler, bench } from 'sloth'

Using requires:

const sloth = require('sloth')

// OR

const { Profiler, bench, benchFile } = require('sloth')

For more detailed descriptions and code examples, see below:


Documentation

Using the Profiler class

The Profiler class is provided as a quick and easy way of profiling any application, provided you have it's PID, although it's aimed more towards profiling the application it's created in.

Creating an instance

const { Profiler } = require('sloth')
const profiler = new Profiler(pid, options)

Options are described below, all are optional:

OptionTypeDescription
toFileBoolean (optional)Whether to export the results to a file or not
timestepNumber (optional)How long, in milliseconds, the memory monitor will check memory usage
waitNumber (optional)How long, in milliseconds, the profiler should wait before returning results
trimNodeProcessUsageBoolean (optional)Takes the memory usage of the node process before anything has happened, and removes that from the data. Should allow for yielding more accurate results in most cases

Methods are described below, all are async:

MethodDescription
startBegins the profiling, returns itself
endStops profiling, returns instance of ProfileResults. See more about ProfileResults here

Examples

Creating a new Profiler instance that will keep track of it's own process's usage:

const { Profiler } = require('sloth')
const profiler = new Profiler(process.pid, {
  timestep: 100,
  wait: 1000,
  // Helps since the profiler will likely skew the results otherwise
  trimNodeProcessUsage: true
})

Profiling the creation of a large array:

await profiler.start()

// 100,000,000 zeros
let myHugeArr = new Array(1e8).fill(0)

const results = (await profiler.end()).results
console.log(results)

Using the Profiler within Jest

const { Profiler } = require('sloth')

// Your Jest test suite
describe('test test', () => {
  // To make the tests cleaner, you should probably make them async
  it ('does something', async () => {
    const profiler = new Profiler(process.pid, {
      timestep: 100,
      wait: 1000,
      // This will shave off all of the memory currently
      // taken up by the Jest test. This way, it'll be more
      // accurate in case you just did a bunch of crazy
      // stuff before and didn't clean it all up properly.
      trimNodeProcessUsage: true
    })

    await profiler.start()

    // 100,000,000 zeros
    let myHugeArr = new Array(1e8).fill(0)

    const results = (await profiler.end()).results

    // Should've taken less than 5 seconds
    // (I doubt it'd actually take that little time, but it's just an example)
    expect(results.time_elapsed < 5000).toBeTruthy()
  })
})

Extra Notes

Depending on how large the functionality being profiled is, you may want the keep the Node garbage collector in mind. Consider the following example:

await profiler.start()

// 100,000,000 zeros
let myHugeArr = new Array(1e8).fill(0)
myHugeArr = null

const results = (await profiler.end()).results
console.log(results)

While the array is set to null, the memory won't actually change unless the garbage collector is run. To change this, run your script with the --expose-gc flag, like so:

node --expose-gc index

And call the garbage collector:

await profiler.start()

// 100,000,000 zeros
let myHugeArr = new Array(1e8).fill(0)
myHugeArr = null

// Important:
global.gc()

const results = (await profiler.end()).results
console.log(results)

Using the bench function

The bench() function takes a function, throws it into a separate process, runs the profiler on the process, and wraps it all together complete with a bow on top*.

* Disclaimer: does not actually provide a bow on top.

Calling bench()

Calling the bench() function is done like so:

const { bench } = require('sloth')
await bench(function, arguments, options)

It will return a ProfileResults instance (see more about ProfileResults here).

For details on options, see Using the Profiler class, as this function uses the exact same options with one important addition:

OptionTypeDescription
setupFunction (optional)Code to run in the "global" scope. Useful for require()s and other otherwise globally defined variables
requirementsArray (optional)A nicer alternative to setup. Each item should be an object with a name (what it's defined as) and path (what is actually requireed)

Examples

* FYI: The word "global" is often in quotes, as it technically isn't global, but instead in the outer scope of the function.

Setting up a test that measures the creation of a large array:

function f() {
  let arr = new Array(1e8).fill(0)
}

// OR

const f = () => {
  let arr = new Array(1e8).fill(0)
}

const results = await bench(f, [], {
  // Less useful, since the process is basically isolated, but still a good idea
  trimNodeProcessUsage: true
})

Testing out an anonymous function:

const results = bench(() => {
  console.log('I work!')
})

// OR

const results = bench(function() {
  console.log('I work!')
})

Using arguments:

function log(text) {
  console.log(text)
}

const results = bench(log, ['I work!'])

// OR 

const results = bench((text) => console.log(text), ['I work!'])

Using a setup function:

// Logging a "globally" defined variable
function f() {
  console.log(myVar)
}

const results = await bench(f, [], {
  setup: () => {
    let myVar = 'I work!'
  }
})
// Using a module imported using require()
function f(data) {
  fs.writeFileSync('text.txt', data, 'utf8')
}

const results = await bench(f, ['I work!'], {
  setup: () => {
    const fs = require('fs')
  }
})

* Linters will probably get pretty pissy at your setup functions, given they define variables that aren't used. Just a heads up.

Using a requirements array

// Using a package
function f() {
  filesystem.writeFileSync('test.txt', 'test')
}

const results = await bench(f, [], {
  requirements: [
    {
      // Parsed as `const filesystem = require('fs')`
      name: 'filesystem',
      path: 'fs'
    }
  ]
})

Extra Notes

When a thread is spawned, it is automatically run with the --expose-gc option and will always run global.gc() once everything has completed, but before the profiler is finished. This is to give a better insight on ending memory usage (end_usage_bytes in the results object.).

Obviously there are security implications when it comes to running code in a serialized-to-unserialized way, even if it's in a separate process and you have complete control of the code going in. Be careful as to how and where you use bench(), sometimes using the Profiler class will be safer.

Benchmarking a file

The benchFile() function takes the existing bench() function and all it's fun function-wrappy goodness, and implements that for an entire file. It allows you to input a path as well as Node AND CLI options, which should allow you control over what exactly is run. This also returns an instance of ProfileResults.

It take three arguments:

OptionTypeDescription
pathStringThe path to the file. This can be relative or absolute, although absolute is much more ideal.
nodeArgsArray (optional)An array of Node options, like --expose-gc. These are options passed to the Node process itself
cliArgsArray (optional)An array of CLI options. These are options likely found or used in your own code.

Examples

Benchmarking a single file

const { benchFile } = require('sloth')

await benchFile('/home/project/myFile.js')

Benchmarking a file and providing some Node arguments

await benchFile(__dirname + '/myFile.js/', [
  '--expose-gc',
  '--no-warnings'
])

Benchmarking a file and providing some CLI arguments

await benchFile(__dirname + '/myFile.js', [], [
  '--do-ten-times',
  '-f',
  '--silent',
  '--input-dir=' + getSomeDir()
])

ProfileResults

Once Profiler.end() or bench() is called, it will return a ProfileResults instance. This contains all of the profiling data, as well as some extra functions that should help aid in viewing and understanding the data.

The values in the ProfileResults.data property are outlined below:

PropertyTypeDescription
startNumberThe timestamp in milliseconds the profiling was started
endNumberThe timestamp in milliseconds the profiling finished
time_elapsedNumberThe amount of time profiling took, in milliseconds
timestep_msNumberThe amount of milliseconds per memory check
mem_listArrayList of memory values collected
startusagebytesNumberThe amount of bytes being used before or as profiling began
peakusagebytesNumberThe largest amount of memory being used at one time
endusagebytesNumberThe amount of memory being used by the end of the profile
baseprocessbytesNumberThe amount of memory used by the process without anything having been done

There are a few methods provided that should help make sense of some of the data:

MethodDescription
averageMemoryUsage()Get average memory usage throughout the whole profile
medianMemoryUsage()Get middle memory usage, when list is sorted least to greatest or greatest to least
modeMemoryUsage()Get most frequently occuring memory value
memoryAtElapsed(ms)Get the amount of memory being used at a certain point in the profile

Extra Notes

If you intend on using this module in automated testing, keep in mind that profiling may take different amounts of time on different machines. This could possibly skew averages and such, so stick to testing on more concrete values like peak_usage_bytes.

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文