WP7 C#/XNA 中的可变字符串?

发布于 2024-12-03 18:43:23 字数 778 浏览 0 评论 0原文

我正在开发 Windows Phone 7 XNA 游戏。它是用 C++ 编写的游戏的移植版,因此,我尝试尽可能少地重写游戏代码。

垃圾在 WP7 上是一个巨大问题,因为收集器是非分代且缓慢的,因此每 MB 分配的收集(每 1MB 触发一次)大约需要 10 毫秒。我完全打算使用最大 90MB 可用空间,因此我们正在考虑每 MB 分配大约 900 毫秒的停顿。

我已经能够重新设计一些东西,这样除了少数字符串之外,我们就不会在每帧中生成垃圾。

似乎 StringBuilder.ToString() 生成垃圾,并且描述的方法 此处不适用于 WP7。

我需要做的两件事是:

  • 将分钟/秒/百的格式设置为 mm:ss.hh 以显示在屏幕上。显然我可以使用 StringBuilder 来做到这一点(使用不会因装箱整数而产生垃圾的扩展方法)并直接使用 SpriteBatch 显示 StringBuilder。
  • 将“foo.bar.baz.qux”形式的字符串拆分为“.”上的数组,即{“foo”,“bar”,“baz”,“qux”},并一次将一个元素复制到字符串数组。这是为了设置游戏参与者的层次状态。它也几乎直接从原始游戏移植而来,并且很大程度上取决于它的工作方式。我真的很想避免重写它。

除了将大量代码转换为使用 char[] 而不是字符串之外,有没有办法在 C# 中拥有真正无垃圾的可变字符串?

I'm working on a Windows Phone 7 XNA game. It's a port of a game written in C++, and as such, I'm trying to do as little rewriting of gameplay code as possible.

Garbage is a huge problem on WP7, because the collector is nongenerational and slow, so a collection (which is triggered every 1MB) takes about 10ms per MB of allocations. I fully intend to be using the maximum 90MB available, so we're looking at a ~900ms stall every MB of allocation.

I've been able to rework things so that we don't have garbage generated per-frame, except for a few cases of strings.

It seems that StringBuilder.ToString() generates garbage, and the method described here doesn't work on WP7.

The two things I need to do are:

  • Format minutes/seconds/hundreths as mm:ss.hh for display to the screen. Apparently I can do that with a StringBuilder (using extension methods that don't create garbage from boxing the ints) and display the StringBuilder directly with SpriteBatch.
  • Split a string of the form "foo.bar.baz.qux" into an array on '.', i.e. {"foo", "bar", "baz", "qux"} and copy one element at a time into an array of strings. This is for setting the hierarchical state of game actors. It's also pretty much directly ported from the original game, and quite a bit depends on it working this way. I'd really like to avoid rewriting it.

Short of converting a lot of code to use char[] instead of string, is there any way to have truly garbage-free mutable strings in C#?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

许久 2024-12-10 18:43:23

为什么首先需要将 StringBuilder 转换为 String ?正如您所注意到的,您可以将 StringBuilder 直接传递到 XNA 的绘图方法中。您还可以使用 StringBuilder 检索子字符串:

substringBuilder.Length = 0;
for (int i = start; i <= end; i++)
    substringBuilder.Append(originalBuilder[i]);

使用 StringBuilder 唯一要避免的事情是 ToString()AppendFormat()。其他格式化方法(据我所知)都不会生成垃圾。 更新:我错了。编写您自己的扩展方法。此处提供了概要:避免垃圾使用 StringBuilder 时

编写一个根据给定分隔符将字符串拆分为子字符串的方法相对容易。请记住,String.Split() 是邪恶的,原因有两个——首先,因为它分配新字符串;其次,因为它分配新的字符串。其次,因为它分配了一个新数组。字符串的不变性只是问题的一半。

考虑 StringBuilder 的以下扩展方法。我还没有彻底测试过这段代码,但它应该可以让您有一个大概的了解。请注意,它希望您传入一个预先分配的数组,然后它将填充该数组。

public static void Substring(this StringBuilder source, Int32 start, Int32 count, StringBuilder output)
{
    output.Length = 0;
    for (int i = start; i < start + count; i++)
    {
        output.Append(source[i]);
    }
}

public static int Split(this StringBuilder source, Char delimiter, StringBuilder[] output)
{
    var substringCount = 0;
    var substringStart = 0;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == delimiter)
        {
            source.Substring(substringStart, i - substringStart, output[substringCount]);
            substringCount++;
            substringStart = i + 1;
        }
    }
    if (substringStart < source.Length - 1)
    {
        source.Substring(substringStart, source.Length - substringStart, output[substringCount]);
        substringCount++;
    }
    return substringCount;
}

Why do you need to convert a StringBuilder to a String in the first place? As you've noted, you can pass StringBuilder directly into XNA's drawing methods. You can also use StringBuilder to retrieve substrings:

substringBuilder.Length = 0;
for (int i = start; i <= end; i++)
    substringBuilder.Append(originalBuilder[i]);

The only things you want to avoid with StringBuilder are ToString() and AppendFormat(). None of the other formatting methods (to my knowledge) generate garbage. Update: I was wrong. Write your own extension methods. A rundown is available here: Avoiding garbage when working with StringBuilder

It's relatively easy to write a method that will split a string into substrings based on a given delimiter. Just remember that String.Split() is evil for two reasons -- first, because it allocates new strings; and second, because it allocates a new array. The immutability of strings is only half of your problem.

Consider the following extension methods for StringBuilder. I haven't thoroughly tested this code, but it should give you a general idea. Note that it expects you to pass in a pre-allocated array, which it will then populate.

public static void Substring(this StringBuilder source, Int32 start, Int32 count, StringBuilder output)
{
    output.Length = 0;
    for (int i = start; i < start + count; i++)
    {
        output.Append(source[i]);
    }
}

public static int Split(this StringBuilder source, Char delimiter, StringBuilder[] output)
{
    var substringCount = 0;
    var substringStart = 0;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == delimiter)
        {
            source.Substring(substringStart, i - substringStart, output[substringCount]);
            substringCount++;
            substringStart = i + 1;
        }
    }
    if (substringStart < source.Length - 1)
    {
        source.Substring(substringStart, source.Length - substringStart, output[substringCount]);
        substringCount++;
    }
    return substringCount;
}
星光不落少年眉 2024-12-10 18:43:23

我正在尝试尽可能少地重写游戏代码。

小心,重写可能需要更长的时间。 Cole Campbell 是对的,当你想绘制它时调用 ToString() 是没有意义的。 XNA 有采用 StringBuldier 的绘制方法,它在我的游戏中运行良好。

问题的另一个解决方案:创建一个字符串的全局 HashSet,并在加载游戏或关卡时设置它,以避免收集压力。

加载游戏时尝试尽可能多地进行操作。

I'm trying to do as little rewriting of gameplay code as possible.

Careful with that it may take longer that rewrite. Cole Campbell is right, there is no point in calling ToString() while you want to draw it. XNA has draw method that takes StringBuldier and it works well in my game.

Another solution to your problem: make a global HashSet of your strings and set up it when you load the game or level to avoid collection pressure.

Try to do as much as possible when you load the game.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文