110 minutes, it seems impossible, or, it's something way too wrong

USER>set ts = $zh f i=1:1:65000000 { set ^YYY(i)="somedata" } write !,"elapsed: ", $zh-ts 

elapsed: 11.126631

USER>s sub="", count = 0, ts = $zh for { set sub = $Order(^YYY(sub)) quit:sub=""  set count = count + 1 } write !,"elapsed: ", $zh-ts 

elapsed: 9.549079

Here result in seconds

Yes, for sure, my example is too simple, and too far from any real situation. 

And there are multiple issues that may happen with your data, it can be how it is stored, where it is stored, how much data in values. And it's difficult to suggest how to check it

Have a look at my series of articles about globals in the database, just for information, it may help understand something, what may go wrong

In any case, there is a right way to count objects, without counting all of them this way. Is using bitmap index, which you can use even if you have own storage, and do not use objects yet. You still able to build own bitmap index, and count items by this index will be at least 64000 times faster, whereas 64000 is just chunk size for bitmap, and speed will vary depends if you don't have much empty spaces between id's, which needs to be numeric

There is no way, to catch the possible issues for the previous version of IRIS.

The best case scenario is if you automate the build process, for instance with Docker, and test a compile stage on different versions of IRIS. But the ability to successfully compile may not prove that it will work, it would be better to have some unit tests, which will check it.

One more thing, may help, to check it, using the such tool as ObjectScript Quality, can help with checking System's API version. Where you can set the oldest supported version of IRIS, and during the scan, it can check if Methods are available in that particular version.

Implemented it with SQL Procedure

CREATE OR REPLACE PROCEDURE %ZDJANGO.CLONE_DATABASE(sourceNS %String, targetNS %String)
LANGUAGE OBJECTSCRIPT
{
	new $namespace
	set $namespace = "%SYS"
	
	$$$ThrowOnError(##class(Config.Namespaces).Get(sourceNS, .sourceNSparams))
	$$$ThrowOnError(##class(Config.Namespaces).Get(targetNS, .targetNSparams))
	
	for kind="Globals", "Routines" {
		$$$ThrowOnError(##class(Config.Databases).Get(sourceNSparams(kind), .sourceDBparams))
		$$$ThrowOnError(##class(Config.Databases).Get(targetNSparams(kind), .targetDBparams))
		
		set from = sourceDBparams("Directory")
		set to = targetDBparams("Directory")
		
		quit:$Data(done(to))
		set done(to) = "" 
		
		$$$ThrowOnError(##class(SYS.Database).Copy(from, to, , , 4))
	}
} 

DANGER: Do not use it, made specifically for my case, it may overwrite database

Is it really the reading file taking so much time or using $piece on the line and setting it to global too?

There are various things here that may slow you, even $increment (best to be replaced by i+1)

You can also split the reading file and set it to global by two parts, and use $sortbegin 

Try to run your code with %SYS.MONLBL started, it will help you understand where it spends more time.

Your issue with screen formatting is very interesting, it deserves a separate topic. I have not seen any issues yet with it. While I’d like to find a way to improve a standard IRIS terminal, like irissqlcli, I’d like to see the most challenging parts like this.

webTerminal obviously not a right tool for it. As a replacement for it, you can try to use ttyd, I use it for irissqlcli-web