Following this post I created [SnakeCase] ActionFilterAttribute, so some of my controllers return JSONs in snake_case.
It works, however what I've found is that it consumes lot of CPU.
Original code:
public class SnakeCaseAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Result is ObjectResult objectResult)
        {
            objectResult.Formatters.Add(new NewtonsoftJsonOutputFormatter(
                new JsonSerializerSettings // this is the culprit
                {
                    ContractResolver = new DefaultContractResolver
                    {
                        NamingStrategy = new SnakeCaseNamingStrategy
                        {
                            OverrideSpecifiedNames = false
                        },
                    },
                },
                context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
                context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
        }
        else
        {
            base.OnActionExecuted(context);
        }
    }
}
It took me few days to find, that following code is about x10-x15 faster!
public class SnakeCaseAttribute : ActionFilterAttribute
{
    private static JsonSerializerSettings jss = new JsonSerializerSettings
    {
        ContractResolver = new DefaultContractResolver
        {
            NamingStrategy = new SnakeCaseNamingStrategy
            {
                OverrideSpecifiedNames = false
            },
        },
    };
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Result is ObjectResult objectResult)
        {
            objectResult.Formatters.Add(new NewtonsoftJsonOutputFormatter(
                jss, // change is here!!!
                context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
                context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
        }
        else
        {
            base.OnActionExecuted(context);
        }
    }
}
Unfortunately, I don't understand why it is so.
I did monitor my app with dotnet-counters and I've found that for the slow case (1st example) there was (apart from huge CPU usage) this weird value:
Time spent in JIT (ms / 1 sec)                                         1051.12
(this value was fluctuating, but always around 1000)
When I changed the code, it is around ~10ms/s (100x times less):
Time spent in JIT (ms / 1 sec)                                         7.162
I suspect that with first example, when JsonSerializerSettings is created every time, it somehow triggers lot of JIT compilation that was using lot of CPU.
I would like to understand this mechanism, and what is really going on inside Newtonsoft's code in that case.