Skip to content

Make non-emitted logs cheaper #377

@Lukasa

Description

@Lukasa

Looking at some perf traces today, I stumbled upon the fact that swift-log appears to be failing at its goal of making non-issued logs approximately free. Consider this code:

public func whyThough(_ logger: Logger) {
    logger.trace("Hello, world!")
}

In an ideal world, in release builds this code would execute almost no instructions when the log level is higher than trace: it would check the log level, and then return.

In practice what it does is:

  • Call swift_beginAccess
  • Call outlined init with copy of Logging.LogHandler, which includes calling through the VWT for the LogHandler to execute its copy constructor
  • Call swift_project_boxed_opaque_existential_1
  • Call swift_destroy_boxed_opaque_existential_1

This appears to all boil down to the fact that the logLevel is held on the LogHandler, not the Logger, so we have to touch the existential in order to load the log level.

In some cases this may not be excessive, but if anyone has inserted trace level logs in hot code paths the cost can spiral quite badly. We’re seeing it take up to 10% of the CPU time in some of our high packet rate benchmarks.

Now, we can resolve this issue by just deleting the trace logging, but I wanted to ask: do we think swift-log should be this expensive? Are we interested in making it faster? We could make it faster by moving the log level to the Logger, not the LogHandler, but conceptually that could break existing code if they were playing weird tricks, and it could cause things that used to fit safely into existentials to not fit. How do we feel?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions