yield return - 内存优化
yield return - memory optimization
还有一个关于 yield return
的问题
所以我需要远程执行不同的 SQL 脚本。脚本在 TFS 中,所以我自动从 TFS 获取它们,并且该过程遍历所有文件,读取它们在内存中的内容并将内容发送到远程 SQL 服务器。
到目前为止,该过程完美无缺。但现在一些脚本将包含 bulk inserts
将脚本的大小增加到 500,000 MB 或更多。
所以我构建了代码"thinking",我曾经在内存中读取过文件的内容,但现在我有了第二个想法。
这是我的(过度简化):
public IEnumerable<SqlScriptSummary> Find(string scriptsPath)
{
if (!Directory.Exists(scriptsPath))
{
throw new DirectoryNotFoundException(scriptsPath);
}
var path = new DirectoryInfo(scriptsPath);
return path.EnumerateFiles("*.sql", SearchOption.TopDirectoryOnly)
.Select(x =>
{
var script = new SqlScriptSummary
{
Name = x.Name,
FullName = x.FullName,
Content = File.ReadAllText(x.FullName, Encoding.Default)
};
return script;
});
}
....
public void ExecuteScripts(string scriptsPath)
{
foreach (var script in Find(scriptsPath))
{
_scriptRunner.Run(script.Content);
}
}
我的理解是 EnumerateFiles
一次会 yield return
每个文件,所以这就是让我 "think" 加载的原因内存中一次一个文件。
但是...
一旦我迭代它们,在 ExecuteScripts
方法中 script
变量会发生什么 foreach
超出范围后循环?那是处置了吗?还是留在记忆中?
如果它保留在内存中,这意味着即使我使用迭代器并在内部使用 yield return
当我遍历所有它们时它们仍然在内存中,对吗?所以最后就像使用 ToList
只是延迟执行是吗?
如果 script
变量在超出范围时被处置,那么我想我会没事的
我如何重新设计代码以优化内存消耗,例如强制一次只将脚本的内容加载到内存中
其他问题:
如何测试(unit/integration 测试)我一次只在内存中加载一个脚本?
我如何测试(unit/integration测试)每个脚本是否released/not从内存中释放?
Once that I'm iterating them, what happens with the script variable used in the foreach loop after it goes out of scope? Is that disposed? or does it remain in memory?
如果您的意思是在 ExecuteScripts
方法中 - 没有什么可处理的,除非 SqlScriptSummary
实现 IDisposable
,这似乎不太可能。但是,这里有两个不同的东西:
script
变量在 foreach
循环后超出范围,不能作为 GC root
script
变量引用的每个对象在没有其他对象引用它时将有资格进行垃圾回收...包括下一次迭代的 script
。
所以是的,基本上应该没问题。您将一次加载一个文件,就 GC 无法收集的对象而言,我看不出内存中一次有多个文件内容的任何原因。 (GC 本身是惰性的,因此不太可能一次 恰好 一个脚本在内存中,但您不必担心那方面的事情,因为您的代码确保它不会同时保留对多个脚本的实时引用。)
您可以测试您是否一次只加载一个脚本的方法是尝试使用大型脚本的大型目录(实际上不执行任何操作)。如果您可以处理的脚本多于您的内存,那很好:)
还有一个关于 yield return
所以我需要远程执行不同的 SQL 脚本。脚本在 TFS 中,所以我自动从 TFS 获取它们,并且该过程遍历所有文件,读取它们在内存中的内容并将内容发送到远程 SQL 服务器。
到目前为止,该过程完美无缺。但现在一些脚本将包含 bulk inserts
将脚本的大小增加到 500,000 MB 或更多。
所以我构建了代码"thinking",我曾经在内存中读取过文件的内容,但现在我有了第二个想法。
这是我的(过度简化):
public IEnumerable<SqlScriptSummary> Find(string scriptsPath)
{
if (!Directory.Exists(scriptsPath))
{
throw new DirectoryNotFoundException(scriptsPath);
}
var path = new DirectoryInfo(scriptsPath);
return path.EnumerateFiles("*.sql", SearchOption.TopDirectoryOnly)
.Select(x =>
{
var script = new SqlScriptSummary
{
Name = x.Name,
FullName = x.FullName,
Content = File.ReadAllText(x.FullName, Encoding.Default)
};
return script;
});
}
....
public void ExecuteScripts(string scriptsPath)
{
foreach (var script in Find(scriptsPath))
{
_scriptRunner.Run(script.Content);
}
}
我的理解是 EnumerateFiles
一次会 yield return
每个文件,所以这就是让我 "think" 加载的原因内存中一次一个文件。
但是...
一旦我迭代它们,在 ExecuteScripts
方法中 script
变量会发生什么 foreach
超出范围后循环?那是处置了吗?还是留在记忆中?
如果它保留在内存中,这意味着即使我使用迭代器并在内部使用
yield return
当我遍历所有它们时它们仍然在内存中,对吗?所以最后就像使用ToList
只是延迟执行是吗?如果
script
变量在超出范围时被处置,那么我想我会没事的
我如何重新设计代码以优化内存消耗,例如强制一次只将脚本的内容加载到内存中
其他问题:
如何测试(unit/integration 测试)我一次只在内存中加载一个脚本?
我如何测试(unit/integration测试)每个脚本是否released/not从内存中释放?
Once that I'm iterating them, what happens with the script variable used in the foreach loop after it goes out of scope? Is that disposed? or does it remain in memory?
如果您的意思是在 ExecuteScripts
方法中 - 没有什么可处理的,除非 SqlScriptSummary
实现 IDisposable
,这似乎不太可能。但是,这里有两个不同的东西:
script
变量在foreach
循环后超出范围,不能作为 GC rootscript
变量引用的每个对象在没有其他对象引用它时将有资格进行垃圾回收...包括下一次迭代的script
。
所以是的,基本上应该没问题。您将一次加载一个文件,就 GC 无法收集的对象而言,我看不出内存中一次有多个文件内容的任何原因。 (GC 本身是惰性的,因此不太可能一次 恰好 一个脚本在内存中,但您不必担心那方面的事情,因为您的代码确保它不会同时保留对多个脚本的实时引用。)
您可以测试您是否一次只加载一个脚本的方法是尝试使用大型脚本的大型目录(实际上不执行任何操作)。如果您可以处理的脚本多于您的内存,那很好:)