In short: 

  • Index ZipIDX On (City,State)  sorts by 2 levels City and State and holds a pointer to the  referred record
  • Index ZipIDX On ZipCode [ Data = (City,State) ] sorts just by ZipCode and holds a pointer to the  referred record but also a copy of City and State This is faster if all you want is to map ZIP to City, State and have no need to access what else is in your record

Assuming you have full access to your Windows you may take this approach.

  • install TCPTRACE as a local forward
  • it shows you what it gets from Caché
  • and passes it along to your target system
  • you can be sure it knows the network mechanics
  • if you see your text, but no reply you most likely
  • are a victim of Windows firewalls
    • OUTGOING on your end,
    • INCOMING at the target system
    • OUTGOING for the target reply
    • INCOMING from the target on your side
  • So in the worst case, you need 4 explicit rules ! 
  • Adding TCPTRACE on the target system also improves insight on the other end

You are right,  The example defines 
Class WSCI.CRE Extends (%RegisteredObject, %Net.WebSocket.ICredentials)
but doesn't use it,

According to the class definition of 
%Net.WebSocket.Client you pass it as a parameter
in the %New() method.

 so you code (extending my example may look like this:

 	set cre=##class(WSCI.CRE).%New()
#; feed whatever your credentials need 	
#; init connection 	
 	set ws=##class(%Net.WebSocket.Client).%New(url,cre,evl)

It might be worth checking what class %Net.WebSocket.Client does with your credentials

Point 2. of the previous reply is definitely WRONG!

Point 4. takes that back in some way but leaves it ambiguous.

Using extended Global References (also in Class Storage Definitions)
allows access to any mounted DB if you have access rights.

A personal example:

USER>for i=1:1:5 set ^|"^^C:\InterSystems\IRIS242\mgr\nonspace"|rcc(i)=i
 
USER>zwrite ^|"^^C:\InterSystems\IRIS242\mgr\nonspace"|rcc
^|"^^C:\InterSystems\IRIS242\mgr\nonspace"|rcc(1)=1
^|"^^C:\InterSystems\IRIS242\mgr\nonspace"|rcc(2)=2
^|"^^C:\InterSystems\IRIS242\mgr\nonspace"|rcc(3)=3
^|"^^C:\InterSystems\IRIS242\mgr\nonspace"|rcc(4)=4
^|"^^C:\InterSystems\IRIS242\mgr\nonspace"|rcc(5)=5
USER>

I learned this traditional technique  47 years ago. 
And it still works fine.

 

much more simple with 2 identic .INT routines a1 and a2​​

ROUTINE a1 [Type=INC]  
load ;
  read !,"loops=",loop,! 
  do t1 hang 0.5 do t2 quit
next 
  set t1=$zh quit 
t1 
  set t0=$zh
  for i=1:1:loop do next
  write t1-t0,!
  quit 
t2 
  set t0=$zh
  for i=1:1:loop do next^a2
  write t1-t0,!
  quit

SAMPLES>d ^a1
loops=1000000
.081626
.136785
SAMPLES>

I just mean you can't do less:
the difference is even worse 40.3%

My approach was rather simple.

  • in runtime any class has its .INT wich has its .OBJ
  • the OBJ is in the partition.
    • if I stay inside the .OBJ  it's fine
    • if I have to load another .OBJ and then reload the original .OBJ it consumes processor cycles
    • both .OBJ can be assumed to be cached, so it's a pure memory exercise
  • the difference of both variants is sub microscopic
    • so looping for 100 M is kind of zoom-in to get something visible
    • The 100 M are common to both scenarios and
    • the Global has only (800 Mb) >>> 8 bytes / record counted by 
  • I decided for SQL Shell for its nice runtime display.

SUMMARY: There is a difference.
But I wouldn't bend a little finger to attack it. (not even on PDP-11)
This is nothing where performance comes from.  

OK - in UDL

IRIS for Windows (x86-64) 2024.3 (Build 217U) Thu Nov 14 2024 17:59:58 EST

ROUTINE anna [Type=INC] 
anna(name) 
 quit ""

A.HUGE.cls

Include anna 
Class A.HUGE Extends (%Persistent, %Populate)
{ 
 Property calc As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}},    SqlComputed ]; 

ClassMethod fill(size) As %String [ SqlProc ]
{
 for i=1:1:size set ^A.HUGED(i)=""
 set ^A.HUGED=i
 quit $ZR_"="_@$ZR
} 

ClassMethod test1(val) As %String [ SqlProc ]
{
 quit ##class(A.PERSON).Anna(val)
} 
ClassMethod test2(val) As %String [ SqlProc ]
{
 quit $$anna(val)
} 
Storage Default
{
 <Data name="HUGEDefaultData">
 <Value name="1">
 <Value>%%CLASSNAME</Value>
 </Value>
 </Data>
 <DataLocation>^A.HUGED</DataLocation>
 <DefaultData>HUGEDefaultData</DefaultData>
 <IdLocation>^A.HUGED</IdLocation>
 <IndexLocation>^A.HUGEI</IndexLocation>
 <StreamLocation>^A.HUGES</StreamLocation>
 <Type>%Storage.Persistent</Type>
} 
}

A.PERSON.cls

Include anna 
Class A.PERSON Extends %Persistent
{ 
 Property calc As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}},  SqlComputed ];

ClassMethod fill(size) As %String [ SqlProc ]
{
 for i=1:1:size set ^A.PERSOND(i)=""
 set ^A.PERSOND=i
 quit $ZR_"="_@$ZR
} 

ClassMethod test1(val) As %String [ SqlProc ]
{
 quit ##class(A.PERSON).Anna(val)
} 

ClassMethod Anna(name As %String) As %String
{
 quit $$anna(name)
} 

Storage Default
{
 <Data name="PERSONDefaultData">
 <Value name="1">
 <Value>%%CLASSNAME</Value>
 </Value>
 </Data>
 <DataLocation>^A.PERSOND</DataLocation>
 <DefaultData>PERSONDefaultData</DefaultData>
 <IdLocation>^A.PERSOND</IdLocation>
 <IndexLocation>^A.PERSONI</IndexLocation>
 <StreamLocation>^A.PERSONS</StreamLocation>
 <Type>%Storage.Persistent</Type>
} }

I did this to verify my approach looping over a simulated table of 100 mio rows

  • SQL procedure TEST1 uses an external Class Method based on anna.INC
  • SQL procedure TEST2 uses an internal Class Method based on anna.INC

The difference is evident:

[SQL]SAMPLES>>select A.HUGE_fill(100000000)
18.     select A.HUGE_fill(100000000)
 
| Expression_1 |
| -- |
| ^A.HUGED=100000000 |
 
1 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0008s/5/828/0ms
          execute time(s)/globals/cmds/disk: 18.8332s/100,000,002/200,000,445/0ms
                                query class: %sqlcq.SAMPLES.cls3
---------------------------------------------------------------------------
[SQL]SAMPLES>>select list(A.HUGE_TEST1(ID)) from A.HUGE
19.     select list(A.HUGE_TEST1(ID)) from A.HUGE
 
| Aggregate_1 |
| -- |
|  |
 
1 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0005s/4/141/0ms
          execute time(s)/globals/cmds/disk: 101.5573s/100,000,001/700,000,424/0ms
                                query class: %sqlcq.SAMPLES.cls2
---------------------------------------------------------------------------
[SQL]SAMPLES>>select list(A.HUGE_TEST2(ID)) from A.HUGE
20.     select list(A.HUGE_TEST2(ID)) from A.HUGE
 
| Aggregate_1 |
| -- |
|  |
 
1 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0005s/4/141/0ms
          execute time(s)/globals/cmds/disk: 72.1640s/100,000,001/700,000,424/0ms
                                query class: %sqlcq.SAMPLES.cls1
---------------------------------------------------------------------------
[SQL]SAMPLES>>

Rough calculation: including the code in the class saves ~30% of execution time

my class code

Include anna
Class A.HUGE Extends (%Persistent, %Populate)
{
Property calc As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];
ClassMethod fill(size) As %String [ SqlProc ]
{
	for i=1:1:size set ^A.HUGED(i)=""
	set ^A.HUGED=i
	quit $ZR_"="_@$ZR
}
ClassMethod test1(val) As %String [ SqlProc ]
{
	quit ##class(A.PERSON).Anna(val)
}
ClassMethod test2(val) As %String [ SqlProc ]
{
	quit $$anna(val)
}

The simplified anna,INC just returns NullString to concentrate on code switching

anna(name) 
 quit ""

Loading compiled obj code from cache to partition should not have any remarkable impact.
But you are right by principle ! It's some kind of overhead and not for free.

If you place the affected code into a .INC routine you may share that piece
rather easy over multiple instances.
Though mostly not used in that way any Include may also contain executable code.
For a :MAC routine it's  nothing impressive.
For Class code it's a bit tricky but works as well

example ANNA.INC

anna(name) ;
 write !,"Hello ",name,!
 quit ">>>"_name_"<<<"

example Anna.CLS
 

/// demo for Anna
Include ANNA
Class A.Anna {
ClassMethod demo(name As %String) As %String
{
	quit $$anna(name)
}
}

It works:

SAMPLES>write "===",##class(A.Anna).demo("robert")
===
Hello robert
>>>robert<<<
SAMPLES>

So multiple loading is reduced.

You have of course also the option to compose a Custom Command in %ZLANG***.MAC
I just have no experience of how this impacts partition loading.