- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
简介
我有一个我最喜欢的算法,它是我很久以前做的,我一直在用新的编程语言、平台等编写和重写它作为某种基准。尽管我的主要编程语言是 C#,但我只是完全复制粘贴了代码并稍微更改了语法,用 Java 构建它并发现它的运行速度提高了 1000 倍。
代码
有相当多的代码,但我只展示这个似乎是主要问题的片段:
for (int i = 0; i <= s1.Length; i++)
{
for (int j = i + 1; j <= s1.Length - i; j++)
{
string _s1 = s1.Substring(i, j);
if (tree.hasLeaf(_s1))
...
数据
需要指出的是,此特定测试中的字符串 s1 的长度为 100 万个字符 (1MB)。
测量值
我在 Visual Studio 中分析了我的代码执行情况,因为我认为我构建树的方式或遍历树的方式不是最优的。检查结果后,string _s1 = s1.Substring(i, j);
行似乎占了 90% 以上的执行时间!
额外观察
我注意到的另一个区别是,尽管我的代码是单线程的,但 Java 设法使用所有 8 个内核(100% CPU 利用率)来执行它,而即使使用 Parallel.For() 和多线程技术,我的 C# 代码也设法最多使用 35-40%。由于该算法与内核数量(和频率)成线性比例关系,我对此进行了补偿,Java 中的代码片段的执行速度仍快 100-1000 倍。
推理
我认为发生这种情况的原因与以下事实有关:C# 中的字符串是不可变的,因此 String.Substring() 必须创建一个副本,并且由于它在嵌套的 for 循环中有很多迭代,我认为很多复制和垃圾收集正在进行,但是,我不知道 Substring 在 Java 中是如何实现的。
问题
此时我有哪些选择?没有办法解决子字符串的数量和长度(这已经最大限度地优化)。有没有我不知道的方法(或者数据结构)可以为我解决这个问题?
请求的最小实现(来自评论)
我省略了后缀树的实现,构造复杂度为O(n),遍历复杂度为O(log(n))
public static double compute(string s1, string s2)
{
double score = 0.00;
suffixTree stree = new suffixTree(s2);
for (int i = 0; i <= s1.Length; i++)
{
int longest = 0;
for (int j = i + 1; j <= s1.Length - i; j++)
{
string _s1 = s1.Substring(i, j);
if (stree.has(_s1))
{
score += j - i;
longest = j - i;
}
else break;
};
i += longest;
};
return score;
}
探查器的屏幕截图片段
请注意,这是使用大小为 300.000 个字符的字符串 s1 进行测试的。出于某种原因,100 万个字符在 C# 中永远不会完成,而在 Java 中它只需要 0.75 秒。消耗的内存和垃圾收集的数量似乎并不表示内存问题。峰值约为 400 MB,但考虑到巨大的后缀树,这似乎是正常的。也没有发现奇怪的垃圾收集模式。
最佳答案
问题来源
经过两天三夜的光荣战斗(以及来自评论的惊人想法和想法),我终于设法解决了这个问题!
我想为遇到类似问题的任何人发布一个答案,其中 string.Substring(i, j)
函数不是获取字符串子字符串的可接受解决方案,因为字符串太大并且您负担不起 string.Substring(i, j)
完成的复制(它必须制作一个副本,因为 C# 字符串是不可变的,没有办法绕过它)或 string.Substring(i, j)
在同一个字符串上被多次调用(就像在我的嵌套 for 循环中一样)给垃圾收集器带来了困难,或者就像我的情况一样!
尝试
我已经尝试了很多建议,例如 StringBuilder、Streams、使用 Intptr 和 Marshal
内unsafe{}
block 甚至创建一个 IEnumerable 和 yield 通过在给定位置内的引用返回字符。所有这些尝试最终都失败了,因为必须完成某种形式的数据连接,因为我没有简单的方法可以在不影响性能的情况下逐个字符地遍历我的树。如果有一种方法可以一次跨越一个数组中的多个内存地址,就像您可以在 C++ 中使用一些指针算法那样……除了……(归功于@Ivan Stoev 的评论)
解决方案
解决方案是使用 System.ReadOnlySpan<T>
(不能是 System.Span<T>
,因为字符串是不可变的)除其他外,它允许我们在不创建副本的情况下读取现有数组中内存地址的子数组。
贴出这段代码:
string _s1 = s1.Substring(i, j);
if (stree.has(_s1))
{
score += j - i;
longest = j - i;
}
已更改为以下内容:
if (stree.has(i, j))
{
score += j - i;
longest = j - i;
}
在哪里stree.has()
现在接受两个整数(子字符串的位置和长度)并执行:
ReadOnlySpan<char> substr = s1.AsSpan(i, j);
请注意 substr
变量实际上是对初始 s1
字符子集的引用数组而不是副本! (s1
变量可以从这个函数访问)
请注意,在撰写本文时,我正在使用 C#7.2 和 .NET Framework 4.6.1,这意味着要获得 Span 功能,我必须转到“项目”>“管理 NuGet 包”,勾选“包含预发布”复选框,然后浏览并安装 System.Memory。
重新运行初始测试(在长度为 100 万个字符的字符串上,即 1MB),速度从 2+ 分钟(我在 2 分钟后放弃等待)增加到 ~86 毫秒!!
关于c# - String.Substring() 似乎是这段代码的瓶颈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51673659/
谁能帮我解决这个问题?我有一个 Tomcat 和简单的 JSF 应用程序:https://github.com/gooamoko/jsfbilling/ .当我在 Tomcat 上运行应用程序时,它运
我有两个这样的域类,第一个是 Manager : package com.mnm class Manager { String name; static hasMany = [ pro
当我运行以下代码时,打印输出似乎不正确。 void thread_Calc(int *pos) { printf("recieved %d\n", *pos); sig = -1; man
这个问题在这里已经有了答案: How to access a local variable from a different function using pointers? (10 个答案) 关闭
我编写了一个程序,其中列表构建器方法返回 IEnumerable of string,其中包括大量字符串(100 万个项目),我将其存储在 List of string 中,然后它将所有项目附加到 中
我正在尝试编写一个 IRC 类型的聊天客户端,它具有可以连接到服务器的客户端。我试图让它在本地 atm 上工作(使用 FIFOS 而不是套接字)。 我遇到了以下我似乎无法解决的问题: 接受新的客户端连
我的一个 cronjobs 每天发送一封电子邮件 35 6 * * * cd $EZPUBLISHROOT && $PHP runcronjobs.php -q 2>&1 我停止使用 cron sud
我使用 WPF 打印路径来处理在我们的应用程序中创建的大型图表。整个图表由视觉效果组成。 所谓的“DesignerPaginator”对图表进行分页(非常简单)。 从这一点来说,我做了以下三件事: -
我尝试在更新之前跟踪系统应用程序并使用: public static boolean isSystemApplication(Context ctx, IContent content) {
我在这里附上了一个查询分析结果,https://explain.depesz.com/s/x9BN 这是查询 EXPLAIN ANALYZE SELECT branche
我正在做一个 CXF(spring) 项目 (HUB)。部署后,我可以看到肥皂和休息服务列表,我通过两个地址打开它。一种是使用本地主机,第二种是使用我电脑的 ip。所以我得到了这些输出。 使用本地主机
这是一个 AnyHashable 不支持枚举转换的简单案例。 enum testEnum: String { case Test } let myObject: AnyHashable = t
我的主要目标是比较存储在数据库和 XLSX 文件中的数据。 为此,我按以下方式创建了两个列表: private class ProductList { public string produc
我从 CMake 3.6 更新到任何最新版本 (3.12.0-rc2),现在我的一个程序无法编译。 奇怪的是,错误消息显示了标准库本身中的 undefined symbol 。这是错误消息: Unde
我希望将我的自定义对话框动画化为从特定点出现,但我无法为对话框设置动画。 该对话框是一个基本的 RelativeLayout,设置为 extends Dialog 类中的布局。 正如这里的一些答案所建
我已经在这个论坛上调查过很多类似的问题,但似乎没有一个能解决我的问题。 我会在底部列出我在这个论坛上看到的一些问题页面,但让我先谈谈我对这个问题的看法。 我正在使用 codeigniter v 2.x
我正在尝试在 RHEL 7 上启动一个 docker-compose 项目作为 systemd 服务。这是我的 systemd 脚本 (/etc/systemd/system/wp.service):
这个问题已经有答案了: "Notice: Undefined variable", "Notice: Undefined index", "Warning: Undefined array key",
我正在尝试在 RHEL 7 上启动一个 docker-compose 项目作为 systemd 服务。这是我的 systemd 脚本 (/etc/systemd/system/wp.service):
此问题出现在my last question here之后。我想将每个按钮聚焦和失去焦点背景设置为主菜单(ContentPane 即 JPanel)下方的背景颜色,因此按钮看起来像选项卡。它在不同的环
我是一名优秀的程序员,十分优秀!