5

我希望就如何加快以下功能获得一些建议。具体来说,我希望找到一种更快的方法将数字(主要是双精度数,IIRC 里面有一个 int)转换为字符串以存储为 Listview 子项。就目前而言,这个函数需要 9 秒来处理 16 个订单!绝对疯了,特别是考虑到除了处理 DateTimes 的调用之外,这一切都只是字符串转换。

我认为实际显示 listview 项目的速度很慢,所以我做了一些研究,发现将所有子项目添加到数组中并使用 Addrange 比一次添加项目要快得多。我实施了更改,但没有更好的速度。

然后,我在每条线路周围添加了一些秒表,以准确缩小导致减速的原因;不出所料,对 datetime 函数的调用是最大的减速,但我很惊讶地看到 string.format 调用也非常慢,并且考虑到它们的数量,占了我大部分时间。

    private void ProcessOrders(List<MyOrder> myOrders)
    {
        lvItems.Items.Clear();
        marketInfo = new MarketInfo();
        ListViewItem[] myItems = new ListViewItem[myOrders.Count];
        string[] mySubItems = new string[8];
        int counter = 0;
        MarketInfo.GetTime();
        CurrentTime = MarketInfo.CurrentTime;
        DateTime OrderIssueDate = new DateTime();

        foreach (MyOrder myOrder in myOrders)
        {
            string orderIsBuySell = "Buy";
            if (!myOrder.IsBuyOrder)
                orderIsBuySell = "Sell";
            var listItem = new ListViewItem(orderIsBuySell);

            mySubItems[0] = (myOrder.Name);
            mySubItems[1] = (string.Format("{0:g}", myOrder.QuantityRemaining) + "/" + string.Format("{0:g}", myOrder.InitialQuantity));
            mySubItems[2] = (string.Format("{0:f}", myOrder.Price));
            mySubItems[3] = (myOrder.Local);
            if (myOrder.IsBuyOrder)
            {
                if (myOrder.Range == -1)
                    mySubItems[4] = ("Local");
                else
                    mySubItems[4] = (string.Format("{0:g}", myOrder.Range));
            }
            else
                mySubItems[4] = ("N/A");
            mySubItems[5] = (string.Format("{0:g}", myOrder.MinQuantityToBuy));
            string IssueDateString = (myOrder.DateWhenIssued + " " + myOrder.TimeWhenIssued);
            if (DateTime.TryParse(IssueDateString, out OrderIssueDate))
                mySubItems[6] = (string.Format(MarketInfo.ParseTimeData(CurrentTime, OrderIssueDate, myOrder.Duration)));
            else
                mySubItems[6] = "Error getting date";
            mySubItems[7] = (string.Format("{0:g}", myOrder.ID));
            listItem.SubItems.AddRange(mySubItems);
            myItems[counter] = listItem;
            counter++;

        }
        lvItems.BeginUpdate();
        lvItems.Items.AddRange(myItems.ToArray());
        lvItems.EndUpdate();
    }

这是来自示例运行的时间数据:
0:166686
1:264779
2:273716
3:136698
4:587902
5:368816
6:955478
7: 128981

其中数字等于数组的索引。与这些相比,所有其他行的刻度都非常低,可以忽略不计。

虽然我希望能够使用 string.format 的数字格式来获得漂亮的输出,但我希望能够在我的一生中加载更多的订单列表,所以如果有 string.format 的替代品,那就相当不错了更快,但没有花里胡哨,我完全赞成。


编辑:感谢所有建议 myOrder 类可能使用 getter 方法而不是像我最初想的那样实际存储变量的人。我检查了一下,果然,这就是我放慢速度的原因。虽然我无权访问该类来更改它,但我能够搭载方法调用来填充 myOrders 并将每个变量复制到同一个调用中的列表中,然后在填充我的 Listview 时使用该列表。现在几乎立即填充。再次感谢。

4

5 回答 5

3

我很难相信简单的 string.Format 调用会导致您的缓慢问题 - 它通常是一个非常快的调用,尤其是对于像您大多数人这样简单的调用。

但有一件事可能会给你几微秒的时间......

代替

string.Format("{0:g}", myOrder.MinQuantityToBuy)

myOrder.MinQuantityToBuy.ToString("g")

当您执行单个值的直接格式时,这将起作用,但对于更复杂的调用没有任何好处。

于 2011-03-21T04:17:34.620 回答
0

哇。我现在觉得有点傻。我花了好几个小时把头撞在墙上,试图弄清楚为什么一个简单的字符串操作会花这么长时间。MarketOrders 是(我认为)一个 myOrders 数组,它通过显式调用一个方法来填充,该方法在每秒可以运行的次数方面受到严格限制。我无权访问该代码来检查,但我一直假设 myOrders 是简单的结构,其成员变量在填充 MarketOrders 时分配,因此 string.format 调用只会对现有数据起作用。在阅读所有指向 myOrder 数据访问作为罪魁祸首的回复时,我开始思考它并意识到 MarketOrders 可能只是一个索引,而不是一个数组,并且 myOrder 信息正在按需读取。

感谢所有的答复。我不敢相信这没有发生在我身上。

于 2011-03-21T15:32:49.250 回答
0

我很高兴你的问题得到了解决。但是我对你的方法做了一个小的重构并想出了这个:

    private void ProcessOrders(List<MyOrder> myOrders)
    {
        lvItems.Items.Clear();
        marketInfo = new MarketInfo();
        ListViewItem[] myItems = new ListViewItem[myOrders.Count];
        string[] mySubItems = new string[8];
        int counter = 0;
        MarketInfo.GetTime();
        CurrentTime = MarketInfo.CurrentTime;
        // ReSharper disable TooWideLocalVariableScope
        DateTime orderIssueDate;
        // ReSharper restore TooWideLocalVariableScope

        foreach (MyOrder myOrder in myOrders)
        {
            string orderIsBuySell = myOrder.IsBuyOrder ? "Buy" : "Sell";
            var listItem = new ListViewItem(orderIsBuySell);

            mySubItems[0] = myOrder.Name;
            mySubItems[1] = string.Format("{0:g}/{1:g}", myOrder.QuantityRemaining, myOrder.InitialQuantity);
            mySubItems[2] = myOrder.Price.ToString("f");
            mySubItems[3] = myOrder.Local;

            if (myOrder.IsBuyOrder)
                mySubItems[4] = myOrder.Range == -1 ? "Local" : myOrder.Range.ToString("g");
            else
                mySubItems[4] = "N/A";

            mySubItems[5] = myOrder.MinQuantityToBuy.ToString("g");

            // This code smells:
            string issueDateString = string.Format("{0} {1}", myOrder.DateWhenIssued, myOrder.TimeWhenIssued);
            if (DateTime.TryParse(issueDateString, out orderIssueDate))
                mySubItems[6] = MarketInfo.ParseTimeData(CurrentTime, orderIssueDate, myOrder.Duration);
            else
                mySubItems[6] = "Error getting date";

            mySubItems[7] = myOrder.ID.ToString("g");

            listItem.SubItems.AddRange(mySubItems);
            myItems[counter] = listItem;
            counter++;
        }
        lvItems.BeginUpdate();
        lvItems.Items.AddRange(myItems.ToArray());
        lvItems.EndUpdate();
    }

该方法应进一步重构:

  1. 考虑到控制反转 (IoC) 并使用依赖注入 (DI) 删除外部依赖项;
  2. 为将返回 DateTime 数据类型的 MyOrder 创建新属性“DateTimeWhenIssued”。这应该用来代替连接两个字符串(DateWhenIssued 和 TimeWhenIssued),然后将它们解析为 DateTime;
  3. 重命名 ListViewItem 因为这是一个内置类;
  4. ListViewItem 应该有一个布尔“IsByOrder”的新构造函数:var listItem = new ListViewItem(myOrder.IsBuyOrder)。而不是字符串“Buy”或“Sell”;
  5. "mySubItems" 字符串数组应该替换为一个类以获得更好的可读性和可扩展性;
  6. 最后,foreach(myOrder myOrder 中的 MyOrder)可以替换为“for”循环,因为无论如何您都在使用计数器。除了“for”循环也更快。

希望您不介意我的建议,并且它们在您的情况下是可行的。

PS。你在使用通用数组吗?ListViewItem.SubItems 属性可以是 public List<string> SubItems { get; set; }

于 2011-05-03T12:17:28.333 回答
0

我将所有 string.format 调用放入一个循环中,并且能够在一秒钟内将它们全部运行 100 万次,所以你的问题不是 string.format ......它在你的代码中的其他地方。

也许其中一些属性在其 getter 方法中有逻辑?如果您注释掉列表视图的所有代码,您会得到什么样的时间?

于 2011-03-21T04:23:36.977 回答
0

绝对不是 string.Format 会减慢您的速度。怀疑来自 myOrder 的属性访问。

在其中一个格式调用中,尝试声明一些局部变量并将它们设置为您尝试格式化的属性,然后将这些局部变量传递给 yoru string.Format 并重新计时。您可能会发现您的 string.Format 现在以应有的速度运行。

现在,属性访问通常不需要太多时间来运行。但是,我见过一些记录每个属性访问的类(用于审计跟踪)。检查是否是这种情况,以及是否某些操作阻止您的财产访问权立即返回。

如果有一些操作持有属性访问权限,请尝试将这些操作排队(例如,将日志调用排队)并让后台线程执行它们。立即归还财产访问权。

此外,切勿将运行缓慢的代码(例如复杂的计算)放入属性访问器/获取器中,也不要将具有副作用的代码放入其中。使用该类的人不会意识到它会很慢(因为大多数属性访问速度很快)或有副作用(因为大多数属性访问没有副作用)。如果访问速度很慢,请将访问重命名为 GetXXX() 函数。如果它有副作用,请将该方法命名为传达此事实的名称。

于 2011-03-21T04:32:12.520 回答