试图优化一些循环代码

时间:2014-07-03 20:56:53

标签: objective-c arrays loops optimization enumeration

我必须使用NSArrays:

  • _kundenArray - 持有所有客户(目前约为3000人)
  • _bestellungenMutArr - 持有所有订单(目前约为8000)

~~~~~

EDIT2 - 补充道:

我的数组都通过解析单独的csv文件来填充,所以最初我不知道客户和订单之间的任何关系。

~~~~~

对于每个客户,我都会尝试确定其订单,更具体地说是最后一个订单(日期)。

我估计超过一半的客户没有订单,有些订单有几个,其他订单很多。

一开始我有2个嵌套循环,外部循环遍历所有客户,内部循环遍历所有订单。最终进行了超过(3000 * 8000)次比较(赋予附加代码)。

经过一番思考后,我意识到我只有有效的订单,即每个订单都有客户ID,而对于每个客户ID,我都有一个具有相同ID的现有客户。 为了减少内部循环的开销,我根据客户ID对我的数组进行了排序。

这意味着第一个订单对应于我的第一批客户。 E.g:

  • _kundenArray [0]的客户ID为 115
  • _bestellungenMutArr [0-3]订购了ids 24-27 ,每个订单均由客户 115

然后在数组中收集每个相应的订单,直到我到达订单,其客户ID与我的客户ID不对应。然后我退出(中断)我的循环,从包含所有订单的数组中删除我收集的订单(_bestellungenMutArr)并继续下一个客户。

从数组中删除对象非常快,因为对象在大数组的开头是 ALL 。 (另请参阅图表,说明在ridiculousfish中不同阵列操作here的性能。

检查仪器的时间分析器数据,我发现超过99%的时间花在删除对象上。仪器输出: Instrument's output for my "removeObjectsInArray"-approach

然后我提出了利用enumerateObjectsUsingBlock索引的想法。我没有使用内部循环的快速枚举,而是使用块枚举器。为了在我的内循环中实现相同的开销减少(即从不处理一个订单两次)我跟踪索引,我后来使用该索引为下一个迭代(对于下一个客户)有一个偏移量。这样我绕过去除数组中的对象,我认为这可能是一个非常好的想法。

检查时间分析器输出结果表明它不是: enter image description here

因此,使用removeObjectsInArray方法(大约1500次)从数组中删除对象的变量比仅仅跟踪索引要快8倍?

这是预期还是我错过了什么?

数组删除/快速枚举变体:

- (void) determineLastOrders
{
    for (Kunde * kunde in _kundenArray)
    {
        NSMutableArray *bestellungenToRemove = [[NSMutableArray alloc] init];

        /* go through all (remaining) orders (after the loop the matching will be removed) and determine the next ones to remove */
        for (Bestellung * bestellung in _bestellungenMutArr)
        {
            if ([[bestellung bestKdNr] isEqualToString:kunde.kdnr])
            {
                if ( kunde.lastOrder == nil)   
                {
                    kunde.lastOrder = _orangeDate;  //"init"
                }
                else if ([kunde.lastOrder compare:[bestellung bestDatum]] == NSOrderedAscending)
                {
                    kunde.lastOrder = [bestellung bestDatum];
                }
                //As this Bestellung already has had a date comparison (equal by kdnr)
                //we won't need to visit it again by our next customer
                [bestellungenToRemove addObject:bestellung];
            }
            else
            {   //as all orders are ordered by the customer id we can abort iteration
                //after we went past the current id
                break;
            }
        }
        [_bestellungenMutArr removeObjectsInArray: bestellungenToRemove];
    }
}

和检查索引/块枚举变体:

- (void) determineLastOrders
{
    __block NSUInteger bestIndex = 0;
    for (Kunde * __block kunde in _kundenArray)
    {
        /* go through all (remaining) orders (after the loop the matching will be removed) and determine the next ones to remove */
        [_bestellungenMutArr enumerateObjectsUsingBlock: ^(Bestellung * bestellung, NSUInteger idx, BOOL *stop)
         {
            if (idx >= (bestIndex))
            {
                if ([[bestellung bestKdNr] isEqualToString:kunde.kdnr])
                {
                    if ( kunde.lastOrder == nil)
                    {
                        kunde.lastOrder = _orangeDate;  //"init"
                    }
                    else if ([kunde.lastOrder compare:[bestellung bestDatum]] == NSOrderedAscending)
                    {
                        kunde.lastOrder = [bestellung bestDatum];
                    }                
                }
                else
                {   //as all orders are ordered by the customer id we can abort iteration
                    //after we went past the current id
                    bestIndex = idx+1;
                    *stop = YES;
                }
            }
         }];
    }
}

提前致谢!

编辑:我想到了另外一个问题。目前 - 在我的第一个代码片段中,我总是在每个内部循环之后调用removeObjectsInArray方法。如果客户没有订单,我删除一个空数组(即尝试删除nil?)。 我的猜测是,如果传递一个空数组,方法退出是指令去除,所以这比每次循环检查我的小数组中的内容更有效。或者我错了吗?

2 个答案:

答案 0 :(得分:1)

您的第二个示例更好,但您仍然会为每个客户列出超过您需要的订单,因为enumerateObjectsUsingBlock:...每次都从头开始。 (与您的第一个代码示例不同,其中每个客户的订单数量都会缩小。)请尝试使用enumerateObjectsAtIndexes:...代替,传递使用以bestIndex开头的NSRange创建的索引集。

或者,您可以使用正常的for循环:for (NSUInteger i = bestIndex; i < [_bestellungenMutArr count]; i++),这可能会更快。

答案 1 :(得分:0)

另一个优化级别:

int count = [_bestellungenMutArr count];
for (NSUInteger i = bestIndex; i < count; i++)

为什么?

现在每次都不会遍历[bestellungen MutAte计数]。