In %Stream.* classes setting the Filename property corresponds to calling the LinkToFile() method (see FilenameSet() method).
From LinkToFile() documentation:

The method as its name suggests creates a LINK to an EXISTING file.
....
Also note that if there is currently some temporary data in the old stream when the LinkToFile is called this temporary data will be removed before the stream is linked to this filename.

I think you have two options:

  1. set Filename BEFORE writing to the stream
  2. when you need to save a stream to a specific file, create a new file stream and use CopyFrom() method to copy existing data

For option 2 here is a sample (using %Stream.TmpBinary for the temporary stream):

set tStream = ##class(%Stream.TmpBinary).%New()
do tStream.Write("whatever stream contains")
set finalStream = ##class(%Stream.FileBinary).%New()
set finalStream.Filename="c:\temp\streamtest.txt"
do finalStream.CopyFrom(tStream)
write finalStream.%Save(),!

I noticed that some of the functions in your system are not IRIS built-in function but, evidently, are custom functions implemented in your system, as documented here.

To search and find the class and code that implement this functions you may search classes that extends the Ens.Rule.FunctionSet class using the class reference in your system/server, not in the documentation website.

I'd implement a custom datatype, something like:

Class Community.dt.IntJSON Extends %Integer
{

Parameter JSONTYPE = "string";
ClassMethod JSONToLogical(%val As %String) As %Integer [ CodeMode = expression, ServerOnly = 1 ]
{
..DisplayToLogical(%val)
}

ClassMethod LogicalToJSON(%val As %Integer) As %String [ CodeMode = expression, ServerOnly = 1 ]
{
..LogicalToDisplay(%val)
}

}

Then in your class:

Class Community.json.TestDT Extends (%RegisteredObject, %JSON.Adaptor)
{

Property something As Community.dt.IntJSON(DISPLAYLIST = ",OK,Error,Warning", VALUELIST = ",0,1,2") [ InitialExpression = 0 ];

ClassMethod RunMe()
{
      set obj = ..%New()
      set obj.something = 2
      do obj.%JSONExportToString(.string)
      write "JSON : " _ string,!

      write "Content  : " _ ..somethingLogicalToDisplay(obj.something),!!

      set obj2=..%New()
      do obj2.%JSONImport(string)
      write "Imported something value: ",obj2.something,!
}

}

Result:

EPTEST>d ##class(Community.json.TestDT).RunMe()
JSON : {"something":"Warning"}
Content  : Warning
 
Imported something value: 2

I manage systems that use IIS since more than 15 years and never experienced performance issues.

For a code that takes 15ms having service/call that takes 40ms total round trip to me is way too much (that's 25ms overhead !!).
We have SOAP services that respond in 10ms (local subnet, round trip measured from the caller/client).

I don't recall any special configuration in IIS.

What's the configuration of the Web/CSP Gateway?

I suspect that's too small, but there are MANY missing details to consider and I might be wrong.

gmheap documentation is here.

Note that for new installation the value is 0 (zero), in that case:

the system will configure the size of gmheap to be 3% of the total memory configured for global buffers. The minimum gmheap will be configured to is 307,200 KB (300 MB) and the maximum is 2,097,000 KB (2 GB)

So, your gmheap is about 1/3 of current default.