3

I made an OCR bot for the online game "Conquer Online".

It works fine, however, it works very slowly.

I figured an i7 should have no problem scanning 250, 000 pixels, especially when there are less than 100 instructions per pixel being executed.

Troublingly, the bot takes about 5 seconds per scan of the screen when it should take mere milliseconds.

I've inserted tons of debugging statements throughout the code, including this one:

        public bool searchScreenCoord(Point screenCoord, Bitmap printscreen, List<Point> blacklistedPixels, List<LootableItem> lootableItems,
            List<AttackableMonster> attackableMonsters, ClickTracker ct, bool chkGreens, bool chkBlacks, bool chkLoot)
        {
            Color pixel = printscreen.GetPixel(screenCoord.X, screenCoord.Y);

#if DEBUG_Time
            DateTime pixelStartTime = DateTime.Now;
#endif

            if (chkGreens)
            {
#if DEBUG_Time
                if ((DateTime.Now.Subtract(pixelStartTime).Ticks > 500))
                {
                    File.AppendAllText("C:\\ocr\\stats\\pixelStats.csv", 
                        "green.Checked:" + DateTime.Now.Subtract(pixelStartTime).Ticks.ToString() + "\r\n");
                    pixelStartTime = DateTime.Now;

                }                
#endif

As you can see from the definition, chkGreens is a bool passed in from the doMonsters method/thread and not a call to chkGreenMonsters.Checked which might have had problems because of accessing the GUI thread. I eliminated the repeated cross-thread calls in an attempt to solve my problem.

My debugging file contains the following:

black.Checked   10000
green.Checked   10001
green.Checked   10000
black.Checked   10000
get black word  30001
black.Checked   10001
black.Checked   10001
get black word  10001

For no reason that I can think of, the thread sleeps for 10, 000 ticks between declaring the date time and checking the passed in bool.

It doesn't happen every time searchScreenCoord is called though. Maybe once every 25 or 50 calls.

I guess this could be time slicing with a misbehaving process demanding 10, 000 ticks every so often, but that explanation seems far fetched.

Does anyone know why this is happening to me?

I'd appreciate any advice that could eliminate the dreaded 10, 000 tick naps the app is taking.

Thanks.

  • Wow this is so user specific, its very localized. Can you elaborate on your threading techniques and how you talk to the GUI? – Jeremy Mar 30 '13 at 03:40
  • Talking to the GUI happens once per screen scan. Checkboxes in the GUI are converted into bools. Threading is accomplished with: System.Threading.Thread huntThread = new System.Threading.Thread(doMonstersThread); huntThread.Start(); The doMonstersThread method handles getting values from the GUI, getting a screen grab, then processing the pixels. – user2226270 Mar 30 '13 at 03:47
  • Do you use `Invoke` or `BeginInvoke` for cross thread gui? – Jeremy Mar 30 '13 at 04:03
  • Invoke is called a couple of times before the thread delays occur. Not immediately before either... like WAY before the problem crops up. I'd say there is very little chance that cross-thread issues are causing seemingly random delays in the screen scanning thread. The Invoke code looks like this: control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue }); – user2226270 Mar 30 '13 at 04:09
  • Hmm. I was just double checking. Seems you've really exhausted all the possibilities :) – Jeremy Mar 30 '13 at 04:38

2 Answers2

3

The values returned by DateTime come from the system clock. It only updates when the thread is first put on the cpu, and so you'll see it jump through time 10ms at a time, or 15ms at a time, even though you may make two calls to it that are 5 microsecs apart.

Use the high performance counter, instead, to measure the passage of physical time.

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool QueryPerformanceFrequency(out long frequency);

Call QPF once to figure out how fast QPC changes per second.

time1 = QPC
-stuff-
time2 = QPC
passedTimeSeconds = (time2 - time1) / QPF
antiduh
  • 11,853
  • 4
  • 43
  • 66
  • 1
    +1 this seem like a good candidate for strange time jumps... Definitely use `Stopwatch` instead of DateTime (there is really no good reason to do manual interop... but why not). – Alexei Levenkov Mar 30 '13 at 04:40
1

GetPixel is a very slow way to access pixel data.

See this SO entry for info on how to process pixel data in large chunks:

Fast work with Bitmaps in C#

Community
  • 1
  • 1
holtavolt
  • 4,378
  • 1
  • 26
  • 40
  • While I appreciate the tip for speeding up processing of pixel data, the GetPixel call takes less than one tick to execute. The problem I am experiencing is with strange delays of 10, 000 ticks for no apparent reason. Any idea what might be causing that delay, other than unpleasent windows time slicing? – user2226270 Mar 30 '13 at 03:52
  • Apologies for skipping ahead. Once you retime it (as per the accepted answer), I expect this will be the actual bottleneck. – holtavolt Mar 30 '13 at 16:08