def testfunc3(cmd): if cmd[:1] != 'O': return
And then again, there's this.
def testfunc3(cmd): if cmd[:1] != 'O': return
And then again, there's this.
@foosel I figured it would be some overhead but not 7x!
@OutsourcedGuru Ran it just to see and yeah not much diff there.
100000000 11.742 0.000 11.742 0.000 test.py:12(testfunc3)
100000000 12.606 0.000 12.606 0.000 test.py:2(testfunc)
100000000 27.575 0.000 141.491 0.000 test.py:7(testfunc2)
Well, now you know why I do precompile all regexes and why there are so many string checks followed by regex matching on success in the comm layer
Well, it's a 7% reduction in this case. It just highlights the difference between a single character comparison with that of strings.
And where does startswith fall in that list?
def testfunc4(cmd):
if not cmd.startswith("OCTO"):
return
My money would be on "on par with substring comparison" as I suspect it's pretty much the same as cmd[:4] == 'OCTO'
.
Def some overhead w/ startswith()
but it's not terrible.
ncalls tottime percall cumtime percall filename:lineno(function)
100000000 10.210 0.000 10.210 0.000 test.py:12(testfunc3)
100000000 18.220 0.000 27.262 0.000 test.py:16(testfunc4)
100000000 11.285 0.000 11.285 0.000 test.py:2(testfunc)
100000000 31.945 0.000 181.042 0.000 test.py:7(testfunc2)
import re
def testfunc(cmd):
if cmd[:4] != 'OCTO':
return
def testfunc2(cmd):
match = re.search(r'^(OCTO[0-9]+)(?:\s(.*))?$', cmd)
if match is None:
return
def testfunc3(cmd):
if cmd[:1] != 'O':
return
def testfunc4(cmd):
if cmd.startswith('OCTO'):
return
i = 0
while i < 100000000:
testfunc('OCTO999')
i += 1
i = 0
while i < 100000000:
testfunc2('OCTO999')
i += 1
i = 0
while i < 100000000:
testfunc3('OCTO999')
i += 1
i = 0
while i < 100000000:
testfunc4('OCTO999')
i += 1
Just for ducks...
testfunc5 = lambda x: x[:1] != 'O'
testfunc6 = lambda x: ord(x[:1]) - 79
Now you've gone too far. TOO FAR
I'll update and give it a go on my next print, then report back.
Alright...did a few prints with version 1.0.1. All is working as expected!
Thanks a lot everyone!
Question in regard to the gcode phases. So far it seems that queuing
may be preferred to sending
based on @OllisGit findings, but if you don't have to discard or modify the commands is the sent
phase the best place to not interrupt the serial buffer/queue as that is a callback rather than being in the middle of the communication?
@jneilliii It really depends on what is required. In the case of this plugin the order of execution does and should matter which is why I fixed it. If you need to intercept or check the command prior to anything else manipulating it then queuing
would be better than sent
which would be after all other hooks/manipulations occur.
The trick is to figure out a way to bail out of the hook as early as possible whilst accomplishing the task. That could mean dumping the required data to a buffer and processing it in anther thread.
Thanks @kantlivelong, so basically enqueue the line sent/received/queued, etc. and use a separate thread to monitor the queue and process similar to how Gina did in the TerminalStats plugin on the live streams? I think I should probably do that with BedLevelVisualizer. now.
I didn't think the received phase would have effected the serial communication since it was coming back to octoprint and thought it might already be in a separate thread, but now that I go back and look at the docs, it clearly states it.
@jneilliii I'm not aware of all use cases for BLV but in my case I only view the results when not printing. The recent PR I submitted will bail out early in the process for the processGCODE
hook if the processing
flag is set. flagMeshCollection
could certainly be changed to queuing
. If you wanted to shave down cputime you could do
def flagMeshCollection(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
if cmd[:1] != "@":
return
if cmd != "@BEDLEVELVISUALIZER":
return
self.mesh = []
self.box = []
if not self.mesh_collection_canceled and not self.processing:
self.processing = True
if self.mesh_collection_canceled:
self.mesh_collection_canceled = False
return
self._bedlevelvisualizer_logger.debug("mesh collection started")
self.processing = True
Might just be splitting hairs though.
Cool!
Yeah, I merged your changes because I see where it would benefit when flagMeshCollection
was enabled in processGCODE
but didn't think about the received phase impacting the serial comm. I don't think it's ever been an issue before.
I was previously flagging mesh collection on queuing, but thought it should be as close to the printer receiving the command as possible to avoid stopping collection on a received ok
due to an overlap of communication.