mirror of
https://github.com/codex-storage/nim-codex.git
synced 2025-02-14 13:57:29 +00:00
add simple profiling API
This commit is contained in:
parent
91d186b717
commit
f99a516203
@ -31,10 +31,16 @@ import pkg/codexdht/discv5/spr as spr
|
|||||||
import ../node
|
import ../node
|
||||||
import ../blocktype
|
import ../blocktype
|
||||||
import ../conf
|
import ../conf
|
||||||
|
<<<<<<< HEAD
|
||||||
import ../contracts
|
import ../contracts
|
||||||
import ../manifest
|
import ../manifest
|
||||||
import ../streams/asyncstreamwrapper
|
import ../streams/asyncstreamwrapper
|
||||||
import ../stores/blockstore
|
import ../stores/blockstore
|
||||||
|
=======
|
||||||
|
import ../contracts except `%*`, `%` # imported from contracts/marketplace (exporting ethers)
|
||||||
|
import ../streams
|
||||||
|
import ../utils/asyncprofiler
|
||||||
|
>>>>>>> f7c385f (add simple profiling API)
|
||||||
|
|
||||||
import ./coders
|
import ./coders
|
||||||
import ./json
|
import ./json
|
||||||
@ -183,6 +189,15 @@ proc initDataApi(node: CodexNodeRef, router: var RestRouter) =
|
|||||||
|
|
||||||
await node.retrieveCid(cid.get(), local = false, resp=resp)
|
await node.retrieveCid(cid.get(), local = false, resp=resp)
|
||||||
|
|
||||||
|
return RestApiResponse.response($json, contentType="application/json")
|
||||||
|
|
||||||
|
when chronosFuturesInstrumentation:
|
||||||
|
router.api(
|
||||||
|
MethodGet,
|
||||||
|
"/api/codex/v1/debug/performance") do () -> RestApiResponse:
|
||||||
|
RestApiResponse.response(
|
||||||
|
$(%(getFutureSummaryMetrics())), contentType="application/json")
|
||||||
|
|
||||||
proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
||||||
router.api(
|
router.api(
|
||||||
MethodGet,
|
MethodGet,
|
||||||
|
@ -2,5 +2,5 @@ import ../conf
|
|||||||
|
|
||||||
when chronosFuturesInstrumentation:
|
when chronosFuturesInstrumentation:
|
||||||
import ./asyncprofiler/asyncprofiler
|
import ./asyncprofiler/asyncprofiler
|
||||||
import ./asyncprofiler/utils
|
import ./asyncprofiler/serialization
|
||||||
export asyncprofiler, utils
|
export asyncprofiler, serialization
|
||||||
|
@ -8,8 +8,9 @@ import ../json
|
|||||||
export tables, options, hashes, timer, chronos, SrcLoc
|
export tables, options, hashes, timer, chronos, SrcLoc
|
||||||
|
|
||||||
type
|
type
|
||||||
FutureMetric* = object
|
FutureMetrics* = object
|
||||||
## Holds average timing information for a given closure
|
## Tracks timing information for a single future (typically an async
|
||||||
|
## proc). Created when a future starts, and discarded when a future ends.
|
||||||
closureLoc*: ptr SrcLoc
|
closureLoc*: ptr SrcLoc
|
||||||
created*: Moment
|
created*: Moment
|
||||||
start*: Option[Moment]
|
start*: Option[Moment]
|
||||||
@ -18,7 +19,8 @@ type
|
|||||||
initDuration*: Duration
|
initDuration*: Duration
|
||||||
durationChildren*: Duration
|
durationChildren*: Duration
|
||||||
|
|
||||||
CallbackMetric* = object
|
OverallMetrics* = object
|
||||||
|
## Holds overall execution statistics for all runs of an async proc
|
||||||
totalExecTime* {.serialize.}: Duration
|
totalExecTime* {.serialize.}: Duration
|
||||||
totalRunTime* {.serialize.}: Duration
|
totalRunTime* {.serialize.}: Duration
|
||||||
totalWallTime* {.serialize.}: Duration
|
totalWallTime* {.serialize.}: Duration
|
||||||
@ -26,22 +28,34 @@ type
|
|||||||
maxSingleTime* {.serialize.}: Duration
|
maxSingleTime* {.serialize.}: Duration
|
||||||
count* {.serialize.}: int64
|
count* {.serialize.}: int64
|
||||||
|
|
||||||
MetricsSummary* = Table[ptr SrcLoc, CallbackMetric]
|
MetricsSummary* = Table[ptr SrcLoc, OverallMetrics]
|
||||||
|
|
||||||
var
|
var
|
||||||
perFutureMetrics: Table[uint, FutureMetric]
|
perFutureMetrics: Table[uint, FutureMetrics]
|
||||||
futureSummaryMetrics: MetricsSummary
|
futureSummaryMetrics: MetricsSummary
|
||||||
|
|
||||||
proc getFutureSummaryMetrics*(): MetricsSummary {.gcsafe.} =
|
proc getFutureSummaryMetrics*(): MetricsSummary {.gcsafe.} =
|
||||||
## get a copy of the table of summary metrics for all futures
|
## get a copy of the table of summary metrics for all futures.
|
||||||
{.cast(gcsafe).}:
|
{.cast(gcsafe).}:
|
||||||
futureSummaryMetrics
|
futureSummaryMetrics
|
||||||
|
|
||||||
|
proc addRun(self: var OverallMetrics, run: FutureMetrics) =
|
||||||
|
## Adds metrics for a single run of a given async proc to its OverallMetrics.
|
||||||
|
self.totalExecTime += run.duration
|
||||||
|
self.totalWallTime += Moment.now() - run.created
|
||||||
|
self.totalRunTime += self.totalExecTime + run.durationChildren
|
||||||
|
self.count.inc
|
||||||
|
self.minSingleTime = min(self.minSingleTime, run.duration)
|
||||||
|
self.maxSingleTime = max(self.maxSingleTime, run.duration)
|
||||||
|
# handle overflow
|
||||||
|
if self.count == self.count.typeof.high:
|
||||||
|
self.totalExecTime = ZeroDuration
|
||||||
|
self.count = 0
|
||||||
|
|
||||||
proc setFutureCreate(fut: FutureBase) {.raises: [].} =
|
proc setFutureCreate(fut: FutureBase) {.raises: [].} =
|
||||||
## used for setting the duration
|
## used for setting the duration
|
||||||
{.cast(gcsafe).}:
|
{.cast(gcsafe).}:
|
||||||
let loc = fut.internalLocation[Create]
|
perFutureMetrics[fut.id] = FutureMetrics()
|
||||||
perFutureMetrics[fut.id] = FutureMetric()
|
|
||||||
perFutureMetrics.withValue(fut.id, metric):
|
perFutureMetrics.withValue(fut.id, metric):
|
||||||
metric.created = Moment.now()
|
metric.created = Moment.now()
|
||||||
# echo loc, "; future create "
|
# echo loc, "; future create "
|
||||||
@ -49,7 +63,6 @@ proc setFutureCreate(fut: FutureBase) {.raises: [].} =
|
|||||||
proc setFutureStart(fut: FutureBase) {.raises: [].} =
|
proc setFutureStart(fut: FutureBase) {.raises: [].} =
|
||||||
## used for setting the duration
|
## used for setting the duration
|
||||||
{.cast(gcsafe).}:
|
{.cast(gcsafe).}:
|
||||||
let loc = fut.internalLocation[Create]
|
|
||||||
assert perFutureMetrics.hasKey(fut.id)
|
assert perFutureMetrics.hasKey(fut.id)
|
||||||
perFutureMetrics.withValue(fut.id, metric):
|
perFutureMetrics.withValue(fut.id, metric):
|
||||||
let ts = Moment.now()
|
let ts = Moment.now()
|
||||||
@ -60,7 +73,6 @@ proc setFutureStart(fut: FutureBase) {.raises: [].} =
|
|||||||
proc setFuturePause(fut, child: FutureBase) {.raises: [].} =
|
proc setFuturePause(fut, child: FutureBase) {.raises: [].} =
|
||||||
{.cast(gcsafe).}:
|
{.cast(gcsafe).}:
|
||||||
## used for setting the duration
|
## used for setting the duration
|
||||||
let loc = fut.internalLocation[Create]
|
|
||||||
let childLoc = if child.isNil: nil else: child.internalLocation[Create]
|
let childLoc = if child.isNil: nil else: child.internalLocation[Create]
|
||||||
var durationChildren = ZeroDuration
|
var durationChildren = ZeroDuration
|
||||||
var initDurationChildren = ZeroDuration
|
var initDurationChildren = ZeroDuration
|
||||||
@ -79,47 +91,36 @@ proc setFuturePause(fut, child: FutureBase) {.raises: [].} =
|
|||||||
# the first block of a child iterator also
|
# the first block of a child iterator also
|
||||||
# runs on the parents clock, so we track our first block
|
# runs on the parents clock, so we track our first block
|
||||||
# time so any parents can get it
|
# time so any parents can get it
|
||||||
# echo loc, "; child firstBlock time: ", initDurationChildren
|
|
||||||
|
|
||||||
metric.durationChildren += durationChildren
|
metric.durationChildren += durationChildren
|
||||||
metric.start = none Moment
|
metric.start = none Moment
|
||||||
# echo loc, "; future pause ", if childLoc.isNil: "" else: " child: " & $childLoc
|
|
||||||
|
|
||||||
proc setFutureDuration(fut: FutureBase) {.raises: [].} =
|
proc setFutureDuration(fut: FutureBase) {.raises: [].} =
|
||||||
{.cast(gcsafe).}:
|
{.cast(gcsafe).}:
|
||||||
## used for setting the duration
|
## used for setting the duration
|
||||||
let loc = fut.internalLocation[Create]
|
let loc = fut.internalLocation[Create]
|
||||||
# assert "set duration: " & $loc
|
# assert "set duration: " & $loc
|
||||||
var fm: FutureMetric
|
var runMetrics: FutureMetrics
|
||||||
# assert perFutureMetrics.pop(fut.id, fm)
|
|
||||||
perFutureMetrics.withValue(fut.id, metric):
|
perFutureMetrics.withValue(fut.id, metric):
|
||||||
fm = metric[]
|
runMetrics = metric[]
|
||||||
|
|
||||||
discard futureSummaryMetrics.hasKeyOrPut(loc, CallbackMetric(minSingleTime: InfiniteDuration))
|
discard futureSummaryMetrics.hasKeyOrPut(loc,
|
||||||
|
OverallMetrics(minSingleTime: InfiniteDuration))
|
||||||
futureSummaryMetrics.withValue(loc, metric):
|
futureSummaryMetrics.withValue(loc, metric):
|
||||||
# echo loc, " set duration: ", futureSummaryMetrics.hasKey(loc)
|
metric[].addRun(runMetrics)
|
||||||
metric.totalExecTime += fm.duration
|
|
||||||
metric.totalWallTime += Moment.now() - fm.created
|
|
||||||
metric.totalRunTime += metric.totalExecTime + fm.durationChildren
|
|
||||||
# echo loc, " child duration: ", fm.durationChildren
|
|
||||||
metric.count.inc
|
|
||||||
metric.minSingleTime = min(metric.minSingleTime, fm.duration)
|
|
||||||
metric.maxSingleTime = max(metric.maxSingleTime, fm.duration)
|
|
||||||
# handle overflow
|
|
||||||
if metric.count == metric.count.typeof.high:
|
|
||||||
metric.totalExecTime = ZeroDuration
|
|
||||||
metric.count = 0
|
|
||||||
|
|
||||||
onFutureCreate =
|
onFutureCreate =
|
||||||
proc (f: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
proc (f: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
||||||
f.setFutureCreate()
|
f.setFutureCreate()
|
||||||
|
|
||||||
onFutureRunning =
|
onFutureRunning =
|
||||||
proc (f: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
proc (f: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
||||||
f.setFutureStart()
|
f.setFutureStart()
|
||||||
|
|
||||||
onFuturePause =
|
onFuturePause =
|
||||||
proc (f, child: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
proc (f, child: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
||||||
# echo "onFuturePause: ", f.pointer.repr, " ch: ", child.pointer.repr
|
|
||||||
f.setFuturePause(child)
|
f.setFuturePause(child)
|
||||||
|
|
||||||
onFutureStop =
|
onFutureStop =
|
||||||
proc (f: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
proc (f: FutureBase) {.nimcall, gcsafe, raises: [].} =
|
||||||
f.setFuturePause(nil)
|
f.setFuturePause(nil)
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
## Utilities for serializing profiler metrics.
|
||||||
|
|
||||||
|
import std/json
|
||||||
|
|
||||||
import asyncprofiler
|
import asyncprofiler
|
||||||
|
|
||||||
import ../json
|
proc `%`*(o: Duration): JsonNode =
|
||||||
|
%(o.nanoseconds)
|
||||||
|
|
||||||
|
proc `%`*(o: cstring): JsonNode =
|
||||||
|
%($(o))
|
||||||
|
|
||||||
proc `%`*(o: MetricsSummary): JsonNode =
|
proc `%`*(o: MetricsSummary): JsonNode =
|
||||||
var rows = newJArray()
|
var rows = newJArray()
|
||||||
@ -11,9 +18,3 @@ proc `%`*(o: MetricsSummary): JsonNode =
|
|||||||
rows.add(row)
|
rows.add(row)
|
||||||
|
|
||||||
rows
|
rows
|
||||||
|
|
||||||
proc `%`*(o: Duration): JsonNode =
|
|
||||||
%(o.nanoseconds)
|
|
||||||
|
|
||||||
proc `%`*(o: cstring): JsonNode =
|
|
||||||
%($(o))
|
|
60
tests/codex/utils/asyncprofiler/testserialization.nim
Normal file
60
tests/codex/utils/asyncprofiler/testserialization.nim
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import std/tables
|
||||||
|
import std/unittest
|
||||||
|
import std/json
|
||||||
|
|
||||||
|
import codex/utils/asyncprofiler
|
||||||
|
|
||||||
|
import ../../helpers
|
||||||
|
|
||||||
|
checksuite "asyncprofiler utils":
|
||||||
|
|
||||||
|
var fooLoc = SrcLoc(
|
||||||
|
procedure: "foo",
|
||||||
|
file: "foo.nim",
|
||||||
|
line: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
let metric = OverallMetrics(
|
||||||
|
totalExecTime: 2.seconds,
|
||||||
|
totalRunTime: 2.seconds,
|
||||||
|
totalWallTime: 2.seconds,
|
||||||
|
minSingleTime: 100.nanoseconds,
|
||||||
|
maxSingleTime: 1500.milliseconds,
|
||||||
|
count: 10
|
||||||
|
)
|
||||||
|
|
||||||
|
test "should serialize OverallMetrics":
|
||||||
|
check %metric == %*{
|
||||||
|
"totalExecTime": 2000000000,
|
||||||
|
"totalRunTime": 2000000000,
|
||||||
|
"totalWallTime": 2000000000,
|
||||||
|
"minSingleTime": 100,
|
||||||
|
"maxSingleTime": 1500000000,
|
||||||
|
"count": 10
|
||||||
|
}
|
||||||
|
|
||||||
|
test "should serialize SrcLoc":
|
||||||
|
check %fooLoc == %*{
|
||||||
|
"procedure": "foo",
|
||||||
|
"file": "foo.nim",
|
||||||
|
"line": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
test "should serialize MetricsSummary":
|
||||||
|
var summary: MetricsSummary = {
|
||||||
|
(addr fooLoc): metric
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
check %summary == %*[%*{
|
||||||
|
"location": %*{
|
||||||
|
"procedure": "foo",
|
||||||
|
"file": "foo.nim",
|
||||||
|
"line": 1,
|
||||||
|
},
|
||||||
|
"totalExecTime": 2000000000,
|
||||||
|
"totalRunTime": 2000000000,
|
||||||
|
"totalWallTime": 2000000000,
|
||||||
|
"minSingleTime": 100,
|
||||||
|
"maxSingleTime": 1500000000,
|
||||||
|
"count": 10
|
||||||
|
}]
|
Loading…
x
Reference in New Issue
Block a user