我目前正在开发一个性能非常关键的程序,我决定探索的一条路径可能有助于减少资源消耗,增加了我的工作线程的堆栈大小,因此我可以移动float[]我将要访问的大部分数据堆栈(使用stackalloc).
我已经读过一个线程的默认堆栈大小是1 MB,所以为了移动我所有的float[]s,我必须将堆栈扩展大约50倍(到50 MB~).
我知道这通常被认为是"不安全的"并且不推荐,但在对我的当前代码进行基准测试后,我发现处理速度提高了530%!因此,我不能简单地通过这个选项而不进一步调查,这导致我的问题; 将堆叠增加到如此大的尺寸(可能出现什么问题)有什么危险,我应该采取什么预防措施来减少这种危险?
我的测试代码,
public static unsafe void TestMethod1()
{
float* samples = stackalloc float[12500000];
for (var ii = 0; ii < 12500000; ii++)
{
samples[ii] = 32768;
}
}
public static void TestMethod2()
{
var samples = new float[12500000];
for (var i = 0; i < 12500000; i++)
{
samples[i] = 32768;
}
}
Run Code Online (Sandbox Code Playgroud) 今天的PC有大量的物理RAM,但是C#的堆栈大小对于32位进程只有1 MB而对于64位进程只有4 MB(C#中的堆栈容量).
为什么CLR中的堆栈大小仍然如此有限?
为什么它恰好是1 MB(4 MB)(而不是2 MB或512 KB)?为什么决定使用这些金额?
我对该决定背后的考虑因素和原因感兴趣.
我最近在Python程序员面前发现了一个关于F#的演示文稿,看了之后,我决定自己实现"蚂蚁拼图"的解决方案.
有一只蚂蚁可以在平面网格上走动.蚂蚁可以一次向左,向右,向上或向下移动一个空间.也就是说,从单元格(x,y),蚂蚁可以进入单元格(x + 1,y),(x-1,y),(x,y + 1)和(x,y-1).蚂蚁无法访问x和y坐标的数字之和大于25的点.例如,点(59,79)是不可访问的,因为5 + 9 + 7 + 9 = 30,大于25.问题是:如果从(1000,1000)开始,蚂蚁可以访问多少个点,包括(1000,1000)本身?
$ ocamlopt -unsafe -rectypes -inline 1000 -o puzzle ant.ml
$ time ./puzzle
Points: 148848
real 0m0.143s
user 0m0.127s
sys 0m0.013s
Run Code Online (Sandbox Code Playgroud)
整洁,我的结果与leonardo在D和C++中的实现相同.与leonardo的C++实现相比,OCaml版本的运行速度比C++慢大约2倍.这是好的,因为leonardo使用队列来删除递归.
然后我将代码翻译成F# ......这就是我得到的:
Thanassis@HOME /g/Tmp/ant.fsharp
$ /g/Program\ Files/FSharp-2.0.0.0/bin/fsc.exe ant.fs
Microsoft (R) F# 2.0 Compiler build 2.0.0.0
Copyright (c) Microsoft Corporation. All Rights Reserved.
Thanassis@HOME /g/Tmp/ant.fsharp
$ ./ant.exe
Process is terminated due to StackOverflowException.
Quit …Run Code Online (Sandbox Code Playgroud) 我编写了一个很小的递归F#代码来查看我可以在.NET/Mono下的堆栈中放入多少级别的递归.它只是在精确的2次幂时打印递归深度,所以我发现最大深度在2的范围内.
我在一个具有定义的堆栈空间量的线程中启动代码System.Threading.Thread (ThreadStart, int).在.Net下,每个递归级别似乎需要大约100个字节,而我可以在2G堆栈上获得大约1600万个级别.Mono下的内存使用情况大致相似,但是我只能获得大约3万个级别.增加传递给Thread过去的堆栈大小值600000不会增加递归深度.
ulimit 报告堆栈大小限制为1G.
一个明显的解释是Mono不会服从第二个论点,Thread如果它太大了.有没有人知道如何说服Mono分配一个大堆栈?
代码是微不足道的,但它只是在某些人关心的情况下:
let rec f i =
if popcount i = 1 then // population count is one on exact powers of 2
printf "Got up to %d\n" i
stdout.Flush ()
if i = 1000000000 then 0 else 1 + f (i+1)
Run Code Online (Sandbox Code Playgroud) 当C#decimal是局部变量时,它是否存储在堆上?
decimal结构有16个字节.ValueType值存储在堆栈中,并decimal继承自ValueType.在32位系统上,参考存储空间只有4个字节; 参考存储空间我的意思是下面的方框标记为例如baz或bar

图像来源.
ValueTypes,这是存储价值的地方; 对于引用类型,这null是存储堆内存位置的任一个或引用.decimal在32位系统上只有4个字节可用的情况下,a 的值(16字节大)如何存储在堆栈中?
我的想法出了什么问题?
我想知道在我们得到堆栈溢出异常之前我们可以在c#中执行多少调用
所以我决定写下面的代码
static void Method2(int Calls)
{
if(!Calls.Equals(0))
Method1(--Calls);//if more calls remain call method1 and reduce counter
}
static void Method1(int Calls)
{
if (!Calls.Equals(0))//if more calls remain call method2 and reduce counter
Method2(--Calls);
}
static void Main(string[] args)
{
var Calls= 42994;//number of calls(stack overflow appears for large number)
Method1(Calls);
}
Run Code Online (Sandbox Code Playgroud)
我的问题是编译器如何决定抛出堆栈溢出异常是关于内存限制的?一旦我把42995我得到stackoverflow但这个数字不是恒定所以这是如何工作的?
class Program
{
static void Main(string[] args)
{
Test(0);
}
static void Test(int i)
{
if (i > 30000)
{
return;
}
Test(i + 1);
}
}
Run Code Online (Sandbox Code Playgroud)
为什么在调用上面的示例时获取递归函数并抛出StackOverflowException.
(因为超过默认的递归堆栈大小?)
但我想知道如何解决这个问题.
谢谢.
我有一个流,其下N个字节是 UTF8 编码的字符串。我想以最少的开销创建该字符串。
这有效:
var bytes = new byte[n];
stream.Read(bytes, 0, n); // my actual code checks return value
var str = Encoding.UTF8.GetString(bytes);
Run Code Online (Sandbox Code Playgroud)
在我的基准测试中,我看到花费了大量时间以byte[]临时形式收集垃圾。如果我可以摆脱这些,我可以有效地将我的堆分配减半。
本UTF8Encoding类没有与流工作方法。
如果有帮助,我可以使用不安全的代码。我不能重用一个byte[]缓冲区,如果没有ThreadLocal<byte[]>它似乎引入了比它减轻的更多的开销。我确实需要支持 UTF8(ASCII 不会削减它)。
这里有我缺少的 API 或技术吗?
当我在C#中编写一些代码时,我有一个递归方法,在几千次调用后导致堆栈溢出异常.
所以过了一会儿,我在C++中编写了相同的代码,它没有例外地工作得很好(即使递归调用的实际数量比C#停止的地方大10倍).
C#和C++在处理这个问题上有什么区别?有没有什么方法可以允许在C#中进行更多的递归调用而不抛出异常?
c# ×8
.net ×3
recursion ×3
f# ×2
stack ×2
c++ ×1
callstack ×1
clr ×1
heap-memory ×1
limits ×1
memory ×1
mono ×1
ocaml ×1
performance ×1
stack-memory ×1
stack-size ×1
stream ×1