Instead of being irate on a forum filled with volunteers who are helping you as best they can, you should raise a ticket on the IA customer support portal and take advantage of the advanced level of support they offer for these sort of things.
EDIT: Apologies for the tone of my post. I'm feeling a bit salty today. But I'm not apologetic enough to change it.
Why are you putting these into site-packages? Your own tests show that it's more performant as a project script.
If it needs to be a global function, then put it in the gateway script project and use inheritance. That is the supported and documented method.
If it needs to be for multiple, but not all projects, then put it in its own project and make it inheritable
Whatever this is doesn't seem to have anything to do with Ignition, it's reproducible in standalone Jython.
edit: and although not an explanation, moving this line:
import re
outside the function body makes both functions faster.
and now I wonder if the whole test methodology is flawed and just victim of JVM warmup / hotspot compilation, because switching the order of the functions tested also seems to have affected my results:
import time
from folder1.f1 import checksomething as checksomething1
from test_file import checksomething as checksomething2
value = "ABCABC.4931"
now1 = time.time()
for _ in range(1000):
x = checksomething2(value)
now2 = time.time()
for _ in range(1000):
x = checksomething1(value)
now3 = time.time()
print("Time taken by checksomething2: ", now2 - now1)
print("Time taken by checksomething1: ", now3 - now2)
kevin@DV-55J5DY3-LT:~$ java -jar jython-standalone-2.7.4.jar measure.py
('Time taken by checksomething2: ', 0.06099987030029297)
('Time taken by checksomething1: ', 0.009000062942504883)
(after moving import re
outside the function body)
The import
statement always takes the JVM-wide import lock. I suspect lock contention.
(Any use of import inside a function definition is a mistake, IMNSHO. Except where required by legacy scope, which can simply be avoided.)
Even after adding warmup rounds and removing the import statement, the first function measured is always just a little slower than the second, doesn't matter which one is first. I can't explain that
With enough warmup rounds it really starts to converge though:
import time
from folder1.f1 import checksomething as checksomething1
from test_file import checksomething as checksomething2
from java.lang import System
value = "ABCABC.4931"
# warmup
for _ in range(100000):
x = checksomething1(value)
x = checksomething2(value)
# measure
now1 = System.nanoTime()
for _ in range(10000):
x = checksomething1(value)
now2 = System.nanoTime()
for _ in range(10000):
x = checksomething2(value)
now3 = System.nanoTime()
print("Time taken by checksomething1: ", (now2 - now1) / 1000000.0, "ms")
print("Time taken by checksomething2: ", (now3 - now2) / 1000000.0, "ms")
kevin@DV-55J5DY3-LT:~$ java -jar jython-standalone-2.7.4.jar measure.py
('Time taken by checksomething1: ', 15.275997, 'ms')
('Time taken by checksomething2: ', 13.782017, 'ms')
Instead of passing value
, pass "%s %d" % (value, _)
to force some work in each loop.
Slows it down a little, but still first is faster by a little. Run to run the timing is pretty inconsistent too.
kevin@DV-55J5DY3-LT:~$ java -jar jython-standalone-2.7.4.jar measure.py
('Time taken by checksomething1: ', 78.806076, 'ms')
('Time taken by checksomething2: ', 64.63638, 'ms')
making sure the warmup isn't optimized away (hopefully) doesn't change anything either.
# warmup
def blackhole(x):
x = None
pass
for _ in range(100000):
x = checksomething1(value)
blackhole(x)
x = checksomething2(value)
blackhole(x)
Java running Python running in an Ubuntu VM...
Not GC either. I've gotta go for my morning run, got nerd sniped here.
kevin@DV-55J5DY3-LT:~$ java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms4G -Xmx4G -XX:+AlwaysPreTouch -jar jython-standalone-2.7.4.jar measure.py
('Time taken by checksomething1: ', 54.383027, 'ms')
('Time taken by checksomething2: ', 29.919179, 'ms')
Make sure you go first, apparently you'll run faster
this feels like hotspot or something funky with import resolution, probably hotspot if i had to guess (who knows how JVM compilation caches things)
Okay so it seems like the "import re" is impacting the duration differently for a non-module Have been testing this directly on Jython
import re
import time
import checksomething2
from checksomething3 import checksomething as checksomething3
def checksomething1(value):
matches = re.findall(r"(\w+)\.?(\d+)?", value)
if len(matches) >= 1 and len(matches[0]) >= 2 and len(matches[0][0]) > 0:
return str(matches[0][0])
else:
raise Exception("Invalid value '{0}'".format(value))
def checksomething1wimport(value):
import re
matches = re.findall(r"(\w+)\.?(\d+)?", value)
if len(matches) >= 1 and len(matches[0]) >= 2 and len(matches[0][0]) > 0:
return str(matches[0][0])
else:
raise Exception("Invalid value '{0}'".format(value))
value = "ABCABC.4931"
x = checksomething1(value)
now = time.time()
for _ in range(10000):
x = checksomething1(value)
measured = time.time() - now
print("{0: 5f} s, Avg: {1: 5f} s 10000x checksomething1 (inline)".format(measured, measured / 10000.0))
x = checksomething1wimport(value)
now = time.time()
for _ in range(10000):
x = checksomething1wimport(value)
measured = time.time() - now
print("{0: 5f} s, Avg: {1: 5f} s 10000x checksomething1 wimport (inline)".format(measured, measured / 10000.0))
x = checksomething2.checksomething(value)
now = time.time()
for _ in range(10000):
x = checksomething2.checksomething(value)
measured = time.time() - now
print("{0: 5f} s, Avg: {1: 5f} s 10000x checksomething2 (/site-packages/checksomething2.py)".format(measured, measured / 10000.0))
x = checksomething2.checksomethingwimport(value)
now = time.time()
for _ in range(10000):
x = checksomething2.checksomethingwimport(value)
measured = time.time() - now
print("{0: 5f} s, Avg: {1: 5f} s 10000x checksomething2 wimport (/site-packages/checksomething2.py)".format(measured, measured / 10000.0))
x = checksomething3.checksomething(value)
now = time.time()
for _ in range(10000):
x = checksomething3.checksomething(value)
measured = time.time() - now
print("{0: 5f} s, Avg: {1: 5f} s 10000x checksomething3 (/site-packages/checksomething3/checksomething.py)".format(measured, measured / 10000.0))
x = checksomething3.checksomethingwimport(value)
now = time.time()
for _ in range(10000):
x = checksomething3.checksomethingwimport(value)
measured = time.time() - now
print("{0: 5f} s, Avg: {1: 5f} s 10000x checksomething3 wimport (/site-packages/checksomething3/checksomething.py)".format(measured, measured / 10000.0))
checksomething.py
import re
def checksomething(value):
matches = re.findall(r"(\w+)\.?(\d+)?", value)
if len(matches) >= 1 and len(matches[0]) >= 2 and len(matches[0][0]) > 0:
return str(matches[0][0])
else:
raise Exception("Invalid value '{0}'".format(value))
def checksomethingwimport(value):
import re
matches = re.findall(r"(\w+)\.?(\d+)?", value)
if len(matches) >= 1 and len(matches[0]) >= 2 and len(matches[0][0]) > 0:
return str(matches[0][0])
else:
raise Exception("Invalid value '{0}'".format(value))
Results:
0.668000 s, Avg: 0.000067 s 10000x checksomething1 (inline)
0.157000 s, Avg: 0.000016 s 10000x checksomething1 wimport (inline)
0.115000 s, Avg: 0.000012 s 10000x checksomething2 (/site-packages/checksomething2.py)
0.120000 s, Avg: 0.000012 s 10000x checksomething2 wimport (/site-packages/checksomething2.py)
0.089000 s, Avg: 0.000009 s 10000x checksomething3 (/site-packages/checksomething3/checksomething.py)
23.761000 s, Avg: 0.002376 s 10000x checksomething3 wimport (/site-packages/checksomething3/checksomething.py)
EDIT:
Same results in Ignition, so it seems like the 'import re' did it ...