Alessandro71 Well-Known Member Licensed User Longtime User Mar 19, 2019 #1 Despite not being a game, my app has several labels that gets updated several times a second: text is changed, visible and color attributes are changed, position and size are changed. How does Android handles redraws? When I set label.Text, it gets redrawn on screen. Does it get redrawn even if the new text is the same as the current one? Is it worth to check if the new string is different from the previous one, and not set it to avoid a redraw? Is it the same for visibility and color attributes, or does the graphic layer already performs these kind of checks?
Despite not being a game, my app has several labels that gets updated several times a second: text is changed, visible and color attributes are changed, position and size are changed. How does Android handles redraws? When I set label.Text, it gets redrawn on screen. Does it get redrawn even if the new text is the same as the current one? Is it worth to check if the new string is different from the previous one, and not set it to avoid a redraw? Is it the same for visibility and color attributes, or does the graphic layer already performs these kind of checks?
S sorex Expert Licensed User Longtime User Mar 19, 2019 #2 in android you can enable a special mode where you can see how much time it needs for the current redrawings. it's something about vertical lines. I read that google was using or planned to use these meassurements to mark 'badly' designed apps. Upvote 0
in android you can enable a special mode where you can see how much time it needs for the current redrawings. it's something about vertical lines. I read that google was using or planned to use these meassurements to mark 'badly' designed apps.
Erel B4X founder Staff member Licensed User Longtime User Mar 20, 2019 #3 What is the problem? Upvote 0
Alessandro71 Well-Known Member Licensed User Longtime User Mar 20, 2019 #4 Problem is speeding up screen redraws: Is it a waste using something like this to update a text label only if it's changed Code: Copy Public Sub setText(text As String) If (text <> m_textLabel.m_label.Text) Then m_textLabel.m_label.Text = text End If End Sub just to avoid unnecessary screen redraw? Does the graphic framework already does this, so it's a useless optimization? Upvote 0
Problem is speeding up screen redraws: Is it a waste using something like this to update a text label only if it's changed Code: Copy Public Sub setText(text As String) If (text <> m_textLabel.m_label.Text) Then m_textLabel.m_label.Text = text End If End Sub just to avoid unnecessary screen redraw? Does the graphic framework already does this, so it's a useless optimization?
Erel B4X founder Staff member Licensed User Longtime User Mar 21, 2019 #5 Alessandro71 said: so it's a useless optimization? Click to expand... Yes. Alessandro71 said: Problem is speeding up screen redraws: Click to expand... I guess that it is currently too slow, right? Upvote 0
Alessandro71 said: so it's a useless optimization? Click to expand... Yes. Alessandro71 said: Problem is speeding up screen redraws: Click to expand... I guess that it is currently too slow, right?
E emexes Expert Licensed User Longtime User Mar 21, 2019 #6 What I do when I have a rapidly changing item on the screen, eg a counter of incoming bytes, is: everytime a on-screen variable is updated, set a flag to indicate that it needs to be refreshed on-screen Code: Copy For I = 1 to 1000000000 LoopNumber = I RefreshFlag = True DoStuff Sleep(0) or Sleep(1) to enable other stuff to happen too Next and then in a timer tick (somewhere between 3 and 20 times per second, to balance CPU load vs user dazzle) Code: Copy Sub tmrRefresh_Tick If RefreshFlag Then RefreshFlag = False 'clear flag first, just in case any updates occur during the next three lines lblLoopNumber.Text = LoopNumber lblSomethingElse.Text = SomethingElse lblButWaitTheresMore.Text = ButWaitTheresMore & " !!!" End If If RefreshStatusFlag then RefreshStatusFlag = False lblStatusLine.Text = "S1:" & ThisStatus & " S2:" & ThatStatus & " LS:" & LinkStatus End If End Sub This has the side-benefits of: (1) not doing non-string to string conversions if the activity is not on screen (because you disable timers when an activity is paused), and (2) automatically loading up all the screen display views on the first timer tick when an activity is resumed (or started) (assuming you set all refresh flags = True in the activity resume routine) (vs what I often see - and sometimes do myself - is the same code twice: once to initialize the view, and then again to update it) Last edited: Mar 21, 2019 Upvote 0
What I do when I have a rapidly changing item on the screen, eg a counter of incoming bytes, is: everytime a on-screen variable is updated, set a flag to indicate that it needs to be refreshed on-screen Code: Copy For I = 1 to 1000000000 LoopNumber = I RefreshFlag = True DoStuff Sleep(0) or Sleep(1) to enable other stuff to happen too Next and then in a timer tick (somewhere between 3 and 20 times per second, to balance CPU load vs user dazzle) Code: Copy Sub tmrRefresh_Tick If RefreshFlag Then RefreshFlag = False 'clear flag first, just in case any updates occur during the next three lines lblLoopNumber.Text = LoopNumber lblSomethingElse.Text = SomethingElse lblButWaitTheresMore.Text = ButWaitTheresMore & " !!!" End If If RefreshStatusFlag then RefreshStatusFlag = False lblStatusLine.Text = "S1:" & ThisStatus & " S2:" & ThatStatus & " LS:" & LinkStatus End If End Sub This has the side-benefits of: (1) not doing non-string to string conversions if the activity is not on screen (because you disable timers when an activity is paused), and (2) automatically loading up all the screen display views on the first timer tick when an activity is resumed (or started) (assuming you set all refresh flags = True in the activity resume routine) (vs what I often see - and sometimes do myself - is the same code twice: once to initialize the view, and then again to update it)
Alessandro71 Well-Known Member Licensed User Longtime User Mar 22, 2019 #7 this is actually more convoluted than that. i actually have a timer for updating some counters, but the most labels (about 150) needs to be updated synchronously with a data capture cycle, which runs several times a second. i tested 2 versions of the update cycle: 1) directly updating the labels 2) checking previous value before actual update and timed the 2 runs: here are the results (average value over about 1000 samples) 1) 11.02 ms 2) 8.17 ms this is against a total cycle time (data capture + refresh) of about 210 ms so a small optimization seems to be possible i will also check your suggestion about avoiding non-string to string conversions, which can be applied to some of my labels thank you Upvote 0
this is actually more convoluted than that. i actually have a timer for updating some counters, but the most labels (about 150) needs to be updated synchronously with a data capture cycle, which runs several times a second. i tested 2 versions of the update cycle: 1) directly updating the labels 2) checking previous value before actual update and timed the 2 runs: here are the results (average value over about 1000 samples) 1) 11.02 ms 2) 8.17 ms this is against a total cycle time (data capture + refresh) of about 210 ms so a small optimization seems to be possible i will also check your suggestion about avoiding non-string to string conversions, which can be applied to some of my labels thank you
E emexes Expert Licensed User Longtime User Mar 22, 2019 #8 Alessandro71 said: avoiding non-string to string conversions Click to expand... If you can compare the values before translating them to strings, that'd be good, eg: Code: Copy Dim NewValue, OldValue as Double NewValue = reading from sensor or whatever If NewValue <> OldValue Then lblValue.Text = NumberFormat2(NewValue, 1, 3, 3, False) OldValue = NewValue End If will (ignoring time taken to read sensor) be significantly faster than Code: Copy Dim NewText, OldText As String NewText = NumberFormat2(reading from sensor or whatever, 1, 3, 3, False) If NewText.CompareTo(OldText) <> 0 Then lblValue.Text = NewText OldText = NewText End If and that's without taking into account the time taken doing garbage collection of all those discarded temporary strings. Not that it matters here, but: the OldText = NewText string assignment is virtually instantaneous (is just a pointer assignment) and might even be (slightly) faster than the 8-byte Double assignment. Both those times get swamped by the String creation and formatting and disposal operations, though. Upvote 0
Alessandro71 said: avoiding non-string to string conversions Click to expand... If you can compare the values before translating them to strings, that'd be good, eg: Code: Copy Dim NewValue, OldValue as Double NewValue = reading from sensor or whatever If NewValue <> OldValue Then lblValue.Text = NumberFormat2(NewValue, 1, 3, 3, False) OldValue = NewValue End If will (ignoring time taken to read sensor) be significantly faster than Code: Copy Dim NewText, OldText As String NewText = NumberFormat2(reading from sensor or whatever, 1, 3, 3, False) If NewText.CompareTo(OldText) <> 0 Then lblValue.Text = NewText OldText = NewText End If and that's without taking into account the time taken doing garbage collection of all those discarded temporary strings. Not that it matters here, but: the OldText = NewText string assignment is virtually instantaneous (is just a pointer assignment) and might even be (slightly) faster than the 8-byte Double assignment. Both those times get swamped by the String creation and formatting and disposal operations, though.
E emexes Expert Licensed User Longtime User Mar 22, 2019 #9 Alessandro71 said: most labels (about 150) Click to expand... I missed that bit about 150 labels. Yikes! How big is your screen?!?! Are we talking 150 different distinct label texts (eg, numbers) or just a few (eg, "Disabled", "Active", "Alarm")? Nonetheless, I'd be expecting each non-string comparison to take less than a microsecond, including array indexing, and so to check all 150 should take less than a millisecond. Although it's currently using about 6% of your CPU (11 ms per 210 ms?) so... is there other low-hanging fruit in the remaining 199 ms of that cycle? Like, is that the time taken to transfer 150 x 1-byte readings over a serial link? Can you compress that down to using bits instead of bytes? Or just send changed data (plus unchanged data but at a lower rate, or by request only)? Sorry, probably preaching to the converted here. Still, if you'd like a fresh set of eyes on the problem, I'd be interested to look at it. Last edited: Mar 22, 2019 Upvote 0
Alessandro71 said: most labels (about 150) Click to expand... I missed that bit about 150 labels. Yikes! How big is your screen?!?! Are we talking 150 different distinct label texts (eg, numbers) or just a few (eg, "Disabled", "Active", "Alarm")? Nonetheless, I'd be expecting each non-string comparison to take less than a microsecond, including array indexing, and so to check all 150 should take less than a millisecond. Although it's currently using about 6% of your CPU (11 ms per 210 ms?) so... is there other low-hanging fruit in the remaining 199 ms of that cycle? Like, is that the time taken to transfer 150 x 1-byte readings over a serial link? Can you compress that down to using bits instead of bytes? Or just send changed data (plus unchanged data but at a lower rate, or by request only)? Sorry, probably preaching to the converted here. Still, if you'd like a fresh set of eyes on the problem, I'd be interested to look at it.
E emexes Expert Licensed User Longtime User Mar 22, 2019 #10 I'm thinking this is either some industrial monitoring system, or a results/scoring system for your kids' athletics carnival. Is either of those guesses close? edit: ah, vehicle data logging. I used to program dynamometers, including reading OBD using the (then new) ELM chip. Presumably you're using some more-direct link than OBD though, and don't have any say over what format it gets sent in. If you're using a database rather than a plain text or binary file to save the data, and you're storing one reading per record rather than one set of (150?) readings per record, then I could imagine most of that 199 ms is consumed by the database library :-/ But... no problem. Last edited: Mar 22, 2019 Upvote 0
I'm thinking this is either some industrial monitoring system, or a results/scoring system for your kids' athletics carnival. Is either of those guesses close? edit: ah, vehicle data logging. I used to program dynamometers, including reading OBD using the (then new) ELM chip. Presumably you're using some more-direct link than OBD though, and don't have any say over what format it gets sent in. If you're using a database rather than a plain text or binary file to save the data, and you're storing one reading per record rather than one set of (150?) readings per record, then I could imagine most of that 199 ms is consumed by the database library :-/ But... no problem.
Alessandro71 Well-Known Member Licensed User Longtime User Mar 23, 2019 #11 real time driving data visualization, through an already highly optimized OBD data reading process most of the labels are graphic elements, only some of them are text i just tested some of the suggested optimizations, but the results are neglectable in my environment Upvote 0
real time driving data visualization, through an already highly optimized OBD data reading process most of the labels are graphic elements, only some of them are text i just tested some of the suggested optimizations, but the results are neglectable in my environment
E emexes Expert Licensed User Longtime User Mar 23, 2019 #12 Looks good. Certainly not the static industrial indicator panel I'd imagined originally. You said that the label updates were taking 11 ms out of 210, and that you'd managed to squeeze that down another 25% already, so I agree with you, you're not going to save much more out of that area. I assume the word "Yaris" and other static stuff isn't being updated constantly. Are there 150 elements on that screen? Is that one screen of several? Is there stuff hidden behind the main screen that we can't see but is using pixel-moving time to keep updated anyway? Your OBD reading must be pretty highly optimised. My recollection was I could get maybe 20 readings per second if I bent the rules of the ELM protocol by sending the next request before the current request had properly finished. Although most manufacturers were moving to CAN, so that's probably sped things up too. Upvote 0
Looks good. Certainly not the static industrial indicator panel I'd imagined originally. You said that the label updates were taking 11 ms out of 210, and that you'd managed to squeeze that down another 25% already, so I agree with you, you're not going to save much more out of that area. I assume the word "Yaris" and other static stuff isn't being updated constantly. Are there 150 elements on that screen? Is that one screen of several? Is there stuff hidden behind the main screen that we can't see but is using pixel-moving time to keep updated anyway? Your OBD reading must be pretty highly optimised. My recollection was I could get maybe 20 readings per second if I bent the rules of the ELM protocol by sending the next request before the current request had properly finished. Although most manufacturers were moving to CAN, so that's probably sped things up too.
S sorex Expert Licensed User Longtime User Mar 23, 2019 #13 from what I see I don't think you get more than 5 updates a second. why not keep that as reference and update only every 200ms instead of when 1 value changes somewhere in between? I also don't see why you need a database to keep track of 10 values? you'll have less overhead when using just a map. The graph just moves on so you only need the latest value while you move the rest to the right. Upvote 0
from what I see I don't think you get more than 5 updates a second. why not keep that as reference and update only every 200ms instead of when 1 value changes somewhere in between? I also don't see why you need a database to keep track of 10 values? you'll have less overhead when using just a map. The graph just moves on so you only need the latest value while you move the rest to the right.