What you are seeing is not the overhead of Rx - it is just that the resolution of .NET timers is 15 milliseconds. Although you specify a 1ms interval, you are going to get 15ms. This is where the 15 seconds is coming from - 1000 x 15ms = 15s.
In constrast, on my laptop the following outputs 0 for the elapsed time:
var subject = new Subject<Unit>();
var stopwatch = new Stopwatch();
subject.Subscribe(_ => {}, () => {
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds); });
stopwatch.Start();
for(int i = 0; i<1000; i++)
{
subject.OnNext(Unit.Default);
}
subject.OnCompleted();