【问题标题】:Power Shell : What is the most efficient way to use variable / string builder with memory?Power Shell:将变量/字符串生成器与内存一起使用的最有效方法是什么?
【发布时间】:2017-10-01 07:24:33
【问题描述】:

我正在努力学习编程/脚本编写的效率,我知道在后台发生了一些我可能不知道的事情。

$sb = new-object System.text.stringbuilder;
$sb.append("Hello World");
Write-Host $sb.clear().append("Hello World 2");

这样有效率吗 还是在后台执行 .toString()

for (iteration) {

  $temp = "test";
  $temp = $null # Which is better?
  # or 
  clear-variable temp # Which is better?
  $temp = "test2"
  [GC]::Collect(); # Is this needed?
}

【问题讨论】:

  • 您有性能问题吗?你量过吗?你的输入有多大?你对它做什么?您是否将字符串连接识别为慢速部分?因为在任何其他情况下,这个问题都是浪费时间,听起来很刺耳。让它起作用。然后让它快点。除非它已经很快了。
  • 这里有一个提示可以帮助您确定事情是否有所作为 - 使用 Measure-Command 并查看您的更改是否有任何影响(剧透 - 他们不会)。请记住,Powershell 是一种脚本语言。您没有在此处优化汇编中的内部循环。你离处理器有好几层抽象。而且这些都不会超过几微秒。不要提前担心错误的东西。写点东西。
  • 发布的 StringBuilder 代码 sn-p 没有任何意义,因为 StringBuilder 仅在您连接数千或数十万长字符串时才需要,最后您调用 ToString 来生成组合字符串.

标签: powershell variables memory


【解决方案1】:

StringBuilder cmets

Eric Lippert 在这里的回答 How does StringBuilder Work(要求使用 C#,但适用于 PowerShell,因为它是同一个 .Net 类)说它在内部使用了一个部件的链接列表。

这意味着它必须在后台执行ToString() 之类的操作,才能将该数据结构转换为可用的打印字符串。

Here's another StringBuilder discussion - 特别是 BlueRaja 的回答。

代码cmets

for (iteration) {

  $temp = "test";
  $temp = $null # Which is better?
  # or 
  clear-variable temp # Which is better?
  $temp = "test2"
  [GC]::Collect(); # Is this needed?
}
  1. $temp = $null 做了一个简单的赋值,Clear-Variable temp 必须做 cmdlet 名称解析,启动一个 cmdlet,参数绑定,并通过代码,Clear-Variable 有更多的功能,即使你不使用它们。但是做一个分配然后下一个$temp = "test"; $temp = "test2" 可能没有清除值就可以了。

  2. [GC]::Collect() 强制垃圾收集器运行 - 这使得工作发生,做工作比什么都不做需要更长的时间,所以不需要在小范围内让任何事情变得更快。在 PowerShell 中从来不需要(?)因为它总是会在某个时候自动发生 - 但如果您已经将数百 MB 或一些 GB 加载到内存中并且不再需要它,调用 [GC]::Collect() 可能有助于更快地释放它,并且可能会使一些更大的数据处理脚本运行得更快一些。也许吧。

一般cmets

想知道后台发生的事情是值得尊重的——你应该知道,当有更快的方法时,你可能会在没有意识到的情况下做非常慢的事情。

但是@Tomalak 在 cmets 中所说的是明智的 - PowerShell 并不是为了尽可能快而构建的,它的构建是为了方便和可读管理任务,它不是连接多路复用网络服务器或位移 3D 游戏引擎,其存在的原因是“将大量工作封装在几个命令中”。

“方便”是指你用计算机的力量来节省人力。它做得更多,所以你必须少写。也就是说,在某种程度上,意味着为了获得更多便利而刻意权衡变慢。所有脚本语言都是。

可读意味着您希望更喜欢专注于任务的代码,而不是支持专注于幕后机制和触发垃圾收集器或其他东西的代码。再次强调,可用性高于性能。

而且我们在 CPU 之上还有很长的路要走,试试吧:

measure-command { $x = 1 }
measure-command { $x = 1 }

看看 TotalMilliseconds = 第一次运行,5 毫秒,下一次运行 1 毫秒,对我来说。仅通过再次执行它就可以取消 80% 的运行时间。下一次运行,1.3 毫秒 - 无故慢了 30%。

.Net JIT 编译,您的系统上发生的其他任务,这是您对变量分配的微优化,对任何事情都没有影响,更改会消失在噪音中。

由于这种效果,担心 PowerShell 中的微观性能有点浪费时间。

但我确实看到,在您了解这一点之前,您无法知道什么值得担心,什么不值得担心,因此,Tomalak 的解雇“提前停止担心错误的事情”有点像catch-22 - 你不知道什么是错误的! “写点东西” 是很好的建议。缓慢地解决问题的工作代码比花时间拖延担心你未来的代码可能会很慢要好得多。

写!当它又慢又烦人时,然后调查以找到最慢的部分并重写它们。当你做到这一点时,你会在编写新代码时避免最慢的事情,因为你知道它们是你经常重写的模式。

现在试试

Measure-Command { $x = @();  foreach ($i in 1..10000) { $x += $i }}

Measure-Command { $x = foreach ($i in 1..10000) { $i }}

3.5 秒对比 0.015 秒。

哇。

当然,1..10000 在内存中生成了一个巨大的数组,当然我们可以通过使用计数器而不是生成数组来使它更好:

Measure-Command { $x = for ($i=1; $i -le 10000; $i++) { $i }}

等待 0.03 秒。它不仅丑陋且不方便,而且速度也较慢。在 PowerShell 层进行测试和计数比 1..10000 在较低级别所做的任何事情都要糟糕。

值得担心吗?不,因为 0.015 或 0.03 秒实际上是在说“足够快,去看看代码的其他部分,比如读取整个文件十次”。 3秒还值得担心吗?

PowerShell 优化趋向于:

  1. 找到最常见的缓慢陷阱($thing += 循环中的字符串或数组连接,并在开始执行任何处理之前将大的东西完全加载到内存中)并更改它们。数量不多,而且通过练习很容易发现 - 跳到循环所在的位置。

  2. 重新考虑您的算法,以减少使用任何语言的工作量。更难,但会让你获得更大的胜利。作为一个快速的手,任何大的循环和数组相互比较通常都可以更改为使用 Group-Object 或 Hashtables,这往往会在 PowerShell 用于排序和报告的那种类型中出现。

  3. 将其中的一部分推送到 C#。

当您担心 StringBuilder 是否正在调用 .ToString() 以及以一种方式或另一种方式清除变量是否会损害性能时,您要么专注于错误的代码和最慢的位在其他地方,或者整体设计有问题,或者您已经超出了应该转移到另一种语言的地步,因为 Powershell 的级别太高,无法为您提供毫秒级的控制。

【讨论】:

    猜你喜欢
    • 2013-02-12
    • 1970-01-01
    • 2020-12-01
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 2012-04-25
    相关资源
    最近更新 更多