14 Replies Latest reply on Apr 28, 2008 3:08 PM by Production Monkey

    Text member slowness

    Level 7
      Okay, I've got a text object, 16 lines with various tabs in it for columns.
      I've discovered that accessing this text member in order to change things is
      abyssmally slow. E.g.:

      member("Text Object").line[2].word[4] = "Blah"

      takes about 1 second to execute, which is terrible, because with 16 lines
      times 11 columns, it takes about 3 minutes to populate the text once. If I
      do the whole line at once:

      member("Text Object").line[2] = TAB & "a" & TAB & "b" & TAB & "c" & TAB &
      "Blah" etc...

      it executes in about half the time, but still way too slow. The only way I
      can get this to a semi-decent speed is to change the ENTIRE text all at
      once, e.g.:

      member("Text Object").text = TAB & "a" & TAB & "b" & TAB & "c" & TAB & "d" &
      RETURN & TAB & "a" & TAB & "b" & TAB & "c" & TAB & "Blah" etc...

      As expected, the line of code, after a few dozen \ marks, takes up about 2
      pages in the script window, because I had to ditch the repeat-loop that was
      populating the text before. This is ridiculous - it can't be THAT hard to
      change text. Why is this taking so long? I'm seriously considering
      replacing this thing with 176 individual text objects, which seems insane,
      but it would probably perform better. Is there any less-drastic way of
      making this run at an acceptable speed?


        • 1. Re: Text member slowness
          Level 7
          Which version of Director are you using, on which platform?

          When you say "text object", do you mean #text member, #field member,
          #flashComponent, or something else?
          • 2. Re: Text member slowness
            Level 7
            > Which version of Director are you using, on which platform?

            MX2004, WindowsXP

            > When you say "text object", do you mean #text member, #field member,
            > #flashComponent, or something else?

            #text. No anti-aliasing or other fanciness, boring old Arial font. I tried
            with #field, but the lack of tabs and adjustable line-spacing made them
            basically useless in this case. I've also tried separating it into either
            11 separate columns or 16 separate rows. Rows performed better than
            columns, but still not great.

            Single text object, .line[].word[]: 36-40 ticks per draw
            Single text object, .line[]: 24-30 ticks per draw
            Single text object, .text: 8-10 ticks per draw
            11 Separate columns, .line[] = 13-16 ticks per draw
            16 Separate rows, .word[]: 4-5 ticks per draw (This is what I'm settling
            with right now)
            No text changes at all: 0-1 ticks per draw

            So I know it's still the text that's slowing this thing down. I haven't yet
            tried it with 176 separate text objects for every single field (I'd have to
            make a bunch more room on the score just to be able to fit that, and it'd be
            really tedious to set up.

            I suppose another option might be to fill a single text object with all 500+
            lines at the beginning, and then just use the ScrollTop to do the scrolling
            instead of scripting the text changes based on the scroll bar. Of course,
            you'd still end up with a long wait the first time it populated (even
            longer, since we're talking about 500+ lines instead of just the 16 that
            show), but maybe if it were hidden behind a load-screen - still not ideal.
            And the single-text object only performed at an even remotely acceptable
            speed when I set the entire text in one shot - which took a monstrous amount
            of code, and that was just 16 lines. Multiply that to the full 500+ and any
            speed advantage is quickly lost. Seriously, there's got to be a better way
            to do this...

            As for #flashComponents, I'm not touching that. Every time I've used any of
            the #flashComponent objects, it's always a pain - first you have to learn a
            whole new scripting language to interact with them, second, they take over
            your input control (even a flash button object for some reason interrupts
            all keyboard input just by being on the screen), and third they have redraw
            problems up the wazoo. (Half the time when I go to a new frame where the
            flash object doesn't exist, it leaves pieces of itself on the screen for
            some inexplicable reason.) As far as I'm concerned, flashComponents are a
            broken feature which I will never use again unless there's some major
            changes to how they work.


            • 3. Re: Text member slowness
              Level 7
              I wasn't suggesting you go near #flashComponents - particularly if
              you're looking for performance. I just wanted clarification about "text
              object" before progressing.

              Where is the text you're using coming from? It sounds like you're
              populating a #text member with the results of a database query. The
              reason I ask is to try to find a way to optimise alterations. Updating
              it, or creating it, at run-time seems to be a requirement, which
              suggests something dynamic going on.

              Can you provide a cut-down version somewhere for download that focuses
              solely on text and performance?
              • 4. Re: Text member slowness
                Level 7
                > Where is the text you're using coming from? It sounds like you're
                > populating a #text member with the results of a database query. The reason
                > I ask is to try to find a way to optimise alterations. Updating it, or
                > creating it, at run-time seems to be a requirement, which suggests
                > something dynamic going on.
                >
                > Can you provide a cut-down version somewhere for download that focuses
                > solely on text and performance?

                Not easily. At any rate, it is *sort of* a database query, but not really.
                I'm creating the database in Director as a property list, and it's already
                created before any of the text stuff happens. I'm sure that it's not the
                database query part of the equation that's causing the slow down, however,
                as I've placed my tick-counters in such a way that it's only surrounding the
                text-changing parts of the code. Much-simplified pseudo-code example:

                database =
                [[#name:"Alex",#date:"1/2/04"],[#name:"Bob",#date:"3/2/05"],etc....]
                --(All of this is generated before-hand, long before any of the code in
                question takes place.)
                pos = sprite("Slider").pos
                --(changes based on the position of the slider sprite)
                repeat with i = 1 to 16
                record = database[i + pos]
                n = the ticks
                member("Text Member").line .word[1] = record.name
                member("Text Member").line
                .word[2] = record.date
                etc...
                put the ticks - n
                end repeat

                Since it isn't starting its tick-counter until after the database query, the
                number that it puts out should show how long the text-management part is
                taking. If I put those lines around the "record = database[i + X]" line, I
                get 0's and 1's, so that part is happening lightning-quick. It's very
                definitely the text-management that's making it take so long. I was
                thinking it was all the complicated tabs in the text field that were causing
                the issue (8 columns are center-justified, 1 is left-justified, and 2 are
                right-justified), but given that my separate-rows code, complete with tabs
                on each row, worked much faster than the separate-columns code (no tabs,
                just the entire text member justified), I doubt that's the issue. As for
                altering the text at runtime, there will be options to filter the data based
                on the values, so, yes, the database that displays would have to be
                generated on the fly if you used the filters. But that part of it is not
                even implemented yet. (And the creation of the database right now happens
                almost instantly, even though I'm generating 500+ entries with a dozen
                properties each.) The slowdown is undeniably from the text populating.


                • 5. Re: Text member slowness
                  Level 7
                  > The slowdown is undeniably from the text populating.

                  You are most likely correct - which is why I was wanting a file to test
                  to see if I could figure out where and why.

                  Perhaps it's related to member.tabs, perhaps your text update routine
                  could be optimised with put ... into ... or setContents(), but without
                  some code to check against a pre-created member and "database" it
                  remains guesswork. While I'm happy to help, you might need to provide
                  some help, too, before others can.

                  I have seen something similar, but it was back in the days of D8 or 8.5
                  and the #text-related slowdown existed on Win2K but not Win98 (or vice
                  versa) - my memory of the details is fading.

                  Could you not rip your existing text member from your project and copy
                  the updating handler (along with a #movie script that provides sample
                  data to populate it with) so that others can test it? It might be
                  isolated to your machine, or be caused by an OS-related update, or a 3rd
                  party application...
                  • 6. Re: Text member slowness
                    UdoGre
                    Hi Darrel,

                    one thing comes to mind: In your repeat loop, use a string variable, and write it to your text member after the loop. Maybe you can bypass the slowness then a bit.

                    HTH,

                    Udo
                    • 7. Re: Text member slowness
                      cratica Level 1
                      Unfortunately, the text for D11 is even slower. I can't upgrade because of this. :( (bummer)
                      • 8. Re: Text member slowness
                        Level 7
                        > one thing comes to mind: In your repeat loop, use a string variable, and
                        > write it to your text member after the loop. Maybe you can bypass the
                        > slowness then a bit.

                        Belated thanks, but I haven't had the chance to get back to work on this
                        until now - that does help quite a bit, cut my draw-time in half. Still not
                        as fast as I'd like it to be, (still losing 2-3 ticks to this process) but
                        much faster than before. I'm still currently using 16 separate text
                        members, though, so maybe if I use this technique and switch it back to a
                        single text member, it might be even faster - I'll try that and report
                        back...


                        • 9. Re: Text member slowness
                          Level 7
                          Another thought, untested.

                          What if you initialize the text member without tabs, insert the data, then
                          set the tab stops on the member?




                          --
                          Mark A. Boyd
                          Keep-On-Learnin' :)
                          • 10. Re: Text member slowness
                            Level 7
                            > What if you initialize the text member without tabs, insert the data, then
                            > set the tab stops on the member?

                            Not sure at this point whether the tabstops have any effect. Combining it
                            all into a single scrolling text member had a dramatic effect - the
                            scrolling now takes 0-1 ticks, which is perfect. Only problem is I'm paying
                            for it by having a 2-second delay when you first populate the text member -
                            though I can cover this up with a "Loading" text or something. I think the
                            real slow-down now is the color-coding I'm putting on the text - positive
                            numbers green, negative red, etc. This was only eating 1-2 ticks per cycle
                            in my previous iterations because I was only coloring 16 lines at a time.
                            Now that I'm pre-generating the entire text member, I have to color all 500
                            lines at once, and I think that's the only remaining source of slow-down,
                            even if the slow-down is only at the very beginning. (Of course if you use
                            any of the filters once they're implemented, it will have to regenerate and
                            re-color the whole thing again, so I'll have to put in a "Loading" indicator
                            every time.) Is there a faster way to assign colors than this?

                            member("Text Member").line .word[x].color = rgb(255,0,0)

                            Unfortunately, colors are not preserved in a string variable, so this sort
                            of thing has to be done at the member-level.


                            • 11. Re: Text member slowness
                              Level 7
                              > member("Text Member").line .word[x].color = rgb(255,0,0)

                              One slight optimisation is to cache your member reference prior to
                              colouring:
                              tMember = member("Text Member")
                              repeat with l = 1 to whatever
                              tMember.line[l].word[w].color = rgb(255, 0, 0)
                              end repeat

                              You could also cache colours into local variables and add word indices
                              into a list:
                              tWord = 0
                              tRed = rgb(255, 0, 0)
                              tGreen = rgb(0, 255, 0)
                              lRed = []
                              lGreen = []
                              repeat with aNumber in numbers
                              tWord = tWord + 1
                              tString = tString & TAB & string(aNumber)
                              if aNumber < 0 then lRed.append(tWord)
                              else if aNumber > 0 then lGreen.append(tWord)
                              end repeat

                              tMember = member("Text Member")
                              repeat with aWord in lRed
                              tMember.word[aWord].color = tRed
                              end repeat
                              repeat with aWord in lGreen
                              tMember.word[aWord].color = tGreen
                              end repeat

                              Again, it's very difficult to suggest optimisations without you posting
                              some code. I don't understand your reluctance to do this since we're
                              here trying to help you.
                              • 12. Re: Text member slowness
                                Level 7
                                > Again, it's very difficult to suggest optimisations without you posting
                                > some code. I don't understand your reluctance to do this since we're here
                                > trying to help you.

                                It's not a reluctance so much as there's just so much code going on (much of
                                it not related to this issue at all) that it would be somewhat difficult to
                                extract the relevant bits in any meaningful way. But since you insist, here
                                goes: (greatly simplified because nobody wants to go through the mountain of
                                code that goes with this)

                                on drawTable
                                member("Table Text").text = "Loading..."
                                updateStage()
                                str = ""
                                repeat with i = 1 to MPL.count
                                SI = MPL
                                str = str & TAB & SI.Rating & TAB & SI.Size & TAB & SI.Eff & TAB &
                                SI.Comp & TAB &\
                                SI.Area & TAB & SI.Spike & TAB & SI.SellerName & TAB &
                                calcItemBuyPrice(i) & RETURN
                                end repeat
                                delete str.char[str.char.count] --Kills that extra RETURN at the end
                                member("Table Text").text = str
                                repeat with i = 1 to MPL.count
                                SI = MPL

                                member("Table Text").line .word[2..6].color = rgb(0,0,255)
                                if SI.Size > 0 then member("Table Text").line
                                .word[2].color =
                                rgb(0,255,0)
                                else if SI.Size < 0 then member("Table Text").line .word[2].color =
                                rgb(255,0,0)
                                if SI.Eff > 0 then member("Table Text").line
                                .word[3].color =
                                rgb(0,255,0)
                                else if SI.Eff < 0 then member("Table Text").line .word[3].color =
                                rgb(255,0,0)
                                if SI.Comp > 0 then member("Table Text").line
                                .word[4].color =
                                rgb(0,255,0)
                                else if SI.Comp < 0 then member("Table Text").line .word[4].color =
                                rgb(255,0,0)
                                if SI.Area > 0 then member("Table Text").line
                                .word[5].color =
                                rgb(0,255,0)
                                if SI.Spike > 0 then member("Table Text").line .word[6].color =
                                rgb(255,0,0)
                                member("Table Text").line
                                .word[8].color = rgb(255,255,0)
                                end repeat
                                member("Table Text").scrollTop = 0
                                end

                                You don't need to see the calcItemBuyPrice function, it just does a bit of
                                basic arithmetic and returns a number.

                                Okay, so MPL is the master list, and SI is used to refer to a Single Item in
                                that list. (I kept the name short in order to fit it in that long line
                                without too many \'s) All the properties shown are properties from the MPL,
                                which are copied over to the SI when we're dealing with just one record.
                                Most of the numbers display green for positive, red for negative, and blue
                                for 0. The Area and Spike properties are never negative, but display green
                                or red respectively for values above 0, and blue for 0. The first number
                                showing Rating and the 7th showing the SellerName are always white. The
                                last column showing the price is always yellow. (There's actually 3 more
                                columns, but they're always white and I'm trying to keep it simple here.)

                                Now, I realize there are two repeat loops that go through the entire list,
                                but the first takes very little time. (4-5 ticks, not bad for a one-time
                                thing.) It's the second loop with the color settings that's killing the
                                load-time, average 120 ticks. Your method of separating it out to only go
                                through the ones that need color changing doesn't seem like it would be that
                                effective, since nearly every figure on the list is colored, and since
                                there's multiple items per line, it would actually be a longer list to go
                                through with that method. (Several longer lists, actually.) As for keeping
                                the rgb values as separate variables - would that actually help? Or would
                                it be faster to use the indexed colors? (Since it's all pure red, green,
                                blue, yellow, white, the limited number of colors in the palette index is
                                actually sufficient.) I'll try a few things...


                                • 13. Re: Text member slowness
                                  Level 7
                                  > Or would it be faster to use the indexed colors? (Since it's all pure
                                  > red, green, blue, yellow, white, the limited number of colors in the
                                  > palette index is actually sufficient.) I'll try a few things...

                                  Self response: The answer is "No". Changing to palette indexes (or indeed
                                  variables containing either rgb or indexed colors) doesn't have any
                                  noticeable effect on the speed of operation.


                                  • 14. Re: Text member slowness
                                    Production Monkey Level 3
                                    My guess at the source of the problem is that every time you change a property of the text member such as the color of a word, then director creates a new image object in the background. That is a lot of overhead for the many changes you want to make. The only solution is to operate on a model of the data you want to display and only force a screen update after All changes have been made to your model.

                                    I threw together a proof of concept this morning using a linear list of lists from which I generate html to describe the look of the table/grid. Since only one line of code alters the contents of the text member, it is acceptably fast.

                                    Attach the following behavior to a text member. A mouse click will add ten rows of sample data. A mouse click with the Control key down will randomly change the color of every cell in the grid. The mouseDown handler is just for testing.

                                    Hopefully this will be of use.

                                    -- Colored Data Grid

                                    Property pMe
                                    Property pColumnCnt
                                    Property pDefaultTextColor
                                    Property pBackgroundColor
                                    Property pDataSet
                                    Property pBorderSize


                                    On GetBehaviorDescription me
                                    return "Colored Data Grid."
                                    end GetBehaviorDescription me


                                    On GetPropertyDescriptionList me
                                    Props = [:]
                                    Props[#pColumnCnt] = [#default:6, #format:#integer, #Range:[#Min:1, #Max:20], #comment:"Number Of Columns"]
                                    Props[#pBorderSize] = [#default:1, #format:#integer, #Range:[#Min:0, #Max:4], #comment:"Grid Border Size"]
                                    Props[#pDefaultTextColor] = [#default:color(0,0,0), #format:#color, #comment:"Default Text Color"]
                                    Props[#pBackgroundColor] = [#default:color(255,255,255), #format:#color, #comment:"Background Color"]
                                    return Props
                                    end GetPropertyDescriptionList me



                                    on beginSprite me
                                    pMe = sprite(me.spriteNum)

                                    pDataSet = []
                                    me.refresh()
                                    end beginSprite


                                    -- refresh the display of the text
                                    on refresh me
                                    if pDataSet.count then
                                    -- create Table Html
                                    Table = "<table border=" & pBorderSize & ">"
                                    repeat with Row in pDataSet
                                    put "<tr>" after Table
                                    repeat with Col in Row
                                    put "<td><font color=" & Quote & Col.Color & Quote & ">" & Col.Text & " </font></td>" after Table
                                    end repeat
                                    put "</tr>" after Table
                                    end repeat
                                    put "</table>" after Table
                                    end if

                                    -- create full html string
                                    Full_Html = "<html><body bgcolor=" & Quote & pBackgroundColor.hexString() & Quote & ">" & Table & "</body></html>"

                                    pMe.member.html = Full_Html
                                    end refresh


                                    -- add row of text. ie ["One","Two","Three","Four","Five","Six"]
                                    on addRow me, ListOfStrings
                                    Row = []
                                    repeat with ColNum = 1 to ListOfStrings.count
                                    Row.add([#Text:ListOfStrings[ColNum], #Color:pDefaultTextColor.hexString()])
                                    end repeat

                                    pDataSet.add(Row)

                                    return pDataSet.count -- return number of rows in data set
                                    end addRow



                                    -- set the color of the text for a row
                                    on setRowColor me, RowNum, ColorDef
                                    Row = pDataSet[RowNum]
                                    repeat with ColNum = 1 to pColumnCnt
                                    Row[ColNum].Color = ColorDef.hexString()
                                    end repeat
                                    end setRowColor



                                    -- set the color of the text for a single cell
                                    on setCellColor me, RowNum, ColNum, ColorDef
                                    pDataSet[RowNum][ColNum].Color = ColorDef.hexString()
                                    end setCelColor



                                    -- for testing
                                    on mouseup me
                                    StartTimeMils = the milliseconds

                                    if _key.controlDown then -- randomly change the color of every cell
                                    repeat with RowNum = 1 to pDataSet.count
                                    repeat with ColNum = 1 to pColumnCnt
                                    me.setCellColor(RowNum, ColNum, rgb(random(255),random(255),random(255)))
                                    end repeat
                                    end repeat
                                    else -- add ten more rows of data
                                    aStringList = ["One","Two","Three","Four","Five","Six"]
                                    repeat with cnt=1 to 10
                                    LastRow = me.addRow(aStringList)
                                    end repeat
                                    end if

                                    me.refresh() -- update the screen

                                    put the milliseconds - StartTimeMils & " Milliseconds for " & pDataSet.count & " Rows"
                                    end mouseup