1
\$\begingroup\$

There are 12 gates. Using our face recognition system, we check every person who tries to enter each gates. My MVC web application is to show the result data to the gate-keeper. And this is the important part. The people in the control center look closely at the current situations in real time.

To put it simply, always two connections for a gate.

Development Environment

  • Entity Framework 6
  • ASP.NET MVC5
  • SignalR for bidirectional communication with IIS8.5

Simple Flow

  1. Face recognition completed
  2. The recognition server is going to change a flag on a database
  3. My polling job will catch that change within 0.3 seconds
  4. Send the result data to the clients

while (true)
{
    Thread.Sleep(300);

    using (DisplayModel DPModel = new DisplayModel(NameOrConnstring))
    {
        // Get the result data if there are any flag changes.
        var ResultData = DPModel.GateDisplay
                         .Where(x => x.g_flag != false)
                         .Select(x => new { x.a_acu_data_id, x.g_status }).ToList();

        // If no result data was received and no observers were found ( gate connections ), skip this polling.
        if (ResultData.Count > 0 && Observers.Count > 0)
        {
            // the first loop for each gates.
            foreach (var Gatedata in ResultData)
            {
                string GateName = Gatedata.a_acu_data_id;

                // See if a client has this current gate ID
                if (Observers.ContainsKey(GateName))
                {
                    // Get the result data produced by the face recognition server
                    GateViewDataModel ProcessedData = DPModel.DisplayViewData
                            .Where(x => x.GATE_NUM == GateName)
                            .Select(x => new GateViewDataModel
                            {
                                COMPANY_NAME = x.COMPANY_NAME,
                                NAME = x.NAME,
                                ENRO_IMG = x.ENRO_IMG,
                                GATE_NUM = x.GATE_NUM,
                                LOG_IMG = x.LOG_IMG,
                                G_STATUS = x.G_STATUS,
                                MODE = x.MODE,
                                PERMIT_AREA = x.PERMIT_AREA
                            }).ToList<GateViewDataModel>().First();

                    if (ProcessedData.ENRO_IMG != null && ProcessedData.ENRO_IMG.Length > 0) ProcessedData.CONVERTED_ENRO_IMAGE = Convert.ToBase64String(enc.Decrypt(ProcessedData.ENRO_IMG, key));
                    if (ProcessedData.LOG_IMG != null && ProcessedData.LOG_IMG.Length > 0) ProcessedData.CONVERTED_LOG_IMG = Convert.ToBase64String(enc.Decrypt(ProcessedData.LOG_IMG, key));

                    // No need to send the original binary data.
                    ProcessedData.ENRO_IMG = new byte[] { 0 };
                    ProcessedData.LOG_IMG = new byte[] { 0 };

                    // the second, nested loop for all the connections to this current gate.
                    foreach (KeyValuePair<string, IHubCallerConnectionContext<dynamic>> dic in Observers[GateName])
                    {
                        // Server sent event by SignalR
                        dic.Value.Caller.onReceived(GateName, ProcessedData);
                    }

                    // Initilaize the flag.
                    var Entity = DPModel.GateDisplay.Single(x => x.a_acu_data_id == GateName);
                    if (Entity != null)
                    {
                        Entity.g_flag = false;
                        DPModel.SaveChanges();
                    }
                }
            }
        }
    }
}

This code is set to run forever right after my application startup.

What I just can't change is

The way I receive the result data. It would be ideal if the face recognition server could send the data directly to the each clients every time it finishes the recognition job. But unfortunately, it doesn't and I don't have enough time to change that right now.

Performance

When my polling catches all the changes at once (this will rarely happen though...), there's going to be 12 loops with two nested loops (two clients are supposed to get the data: one is for the gate-keeper and another is for the people in control center). It takes about 0.8s or 1.8s to complete distribution of the result data to each clients.

This is \$O(n^2)\$, isn't it?

\$\endgroup\$
4
  • \$\begingroup\$ When you say "O(n2)", do you mean "O(n^2)"? Or O(2n), AKA O(n)? I'm guessing the former, but I still wanna clarify \$\endgroup\$
    – anon
    Commented Nov 25, 2015 at 1:46
  • \$\begingroup\$ It's former one. "O(n²)" \$\endgroup\$
    – hina10531
    Commented Nov 25, 2015 at 2:23
  • \$\begingroup\$ Theoretically it should be O(n). Each loop adds a linear amount of additional processing. No loop is forced to do more work by the presence of other loops. Whether they are parallel threads or executed sequentially doesn't affect the overall complexity. If 1 thread = 1 loop = 2 * (image handling computation), then 1 thread = O(2n) or O(n). Two threads would then be 2 loops = 4 * (image handling computation), or O(4n) or O(n). More generally, if x = number of loops, then y= 2x, a linear function. For your upper bound, 12 threads = 12 loops = 24n. Still O(n). \$\endgroup\$ Commented Nov 25, 2015 at 3:25
  • \$\begingroup\$ I would either introduce a service bus and let the feeder system push data to you instead of you polling the database. If you cant change the feeder application to support a service bus I would instead use a SQL service broker and use a SQL dep. listener that will invoke your code when new data is avaiable. \$\endgroup\$
    – Anders
    Commented Nov 25, 2015 at 8:10

2 Answers 2

2
\$\begingroup\$

I have a few comments

This

.Where(x => x.g_flag != false)

is more clear like this :

.Where(x => x.g_flag)

In this if statement:

 if (ResultData.Count > 0 && Observers.Count > 0)

is redundant to check for the first condition because if ResultData.Count equals zero the following foreach would take care of that:

 foreach (var Gatedata in ResultData)

This is really hard to read:

  if (ProcessedData.ENRO_IMG != null && ProcessedData.ENRO_IMG.Length > 0) ProcessedData.CONVERTED_ENRO_IMAGE = Convert.ToBase64String(enc.Decrypt(ProcessedData.ENRO_IMG, key));
  if (ProcessedData.LOG_IMG != null && ProcessedData.LOG_IMG.Length > 0) ProcessedData.CONVERTED_LOG_IMG = Convert.ToBase64String(enc.Decrypt(ProcessedData.LOG_IMG, key));

a better formatting would make it much better:

if (ProcessedData.ENRO_IMG != null 
    && ProcessedData.ENRO_IMG.Length > 0)
{   
    var image = enc.Decrypt(ProcessedData.ENRO_IMG, key)
    ProcessedData.CONVERTED_ENRO_IMAGE = Convert.ToBase64String(image);
}    
if (ProcessedData.LOG_IMG != null 
    && ProcessedData.LOG_IMG.Length > 0)
{ 
    var image = enc.Decrypt(ProcessedData.LOG_IMG, key)
    ProcessedData.CONVERTED_LOG_IMG = Convert.ToBase64String(image);
}

Other general points:

  1. Comments are bit excessive.
  2. You should break up this to several functions. (This would help self document your code among many other things).
\$\endgroup\$
2
  • \$\begingroup\$ Comments were added here. I don't use those comments in my own code sheet. Thank you for improving my code redability. \$\endgroup\$
    – hina10531
    Commented Nov 25, 2015 at 4:09
  • \$\begingroup\$ But checking ResultData.Count and Observer.Count is not redundant, I think. There are times when ResultData.Count is more than 1 but no observers are available. Isn't it meaningless to run foreach in that case? \$\endgroup\$
    – hina10531
    Commented Nov 25, 2015 at 4:17
0
\$\begingroup\$

I'm not sure how to improve the processing speed of your source but, you should use the way to select all at the same time.

string[] names = {};
foreach(var Gatedata in resultData)
{
    string GateName = Gatedata.a_acu_data_id;
    if (Observers.ContainsKey(GateName))
    {
        names.Add(GetName);
    }
}

var ProcessedData = DPModel.DisplayViewData.Where(x => names.Contains(x.GATE_NUM));
var Entity = DPModel.GateDisplay.Where(x => names.Contains(x.a_acu_data_id));

foreach(Var item in ProcessedData)
{
    ...
    item .ENRO_IMG = new byte[] { 0 };
    ...
}

foreach(Var item in ProcessedData)
{
    item.g_flag = false; 
}

//you should use "savechanges" just the once
DPModel.SaveChanges();
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.