controlflow / resharper-heapview Goto Github PK
View Code? Open in Web Editor NEWReSharper Heap Allocations Viewer plugin
License: MIT License
ReSharper Heap Allocations Viewer plugin
License: MIT License
For some suggestions, ReSharper offers a "Why is this being suggested" type link.
It would be nice if HeapView offered something similar. For example, I notice it is littering my code with allocations for foreach
loops, Where
clauses in LINQ statements and more. I'm not really sure what level of developer this tool is geared towards, but some form of hints to explain why something is being listed might help the developer make a more informed decision on if they need to investigate further etc.
Or I could just be being an idiot and missing the point of the tool!
Regards;
Richard Moss
I wanted to bring this up to ensure that I am understanding this correctly and/or to confirm that this might be a bug, or at the very least misleading.
I have created a delegate using the new
and passing the delegate in with the constructor:
The Get
is still underlined, even when I use the constructor. Is this expected? It seems to me that by instantiating the delegate directly, that the instantiation is what causes the allocation. That is, the hint should be under the new
rather than the method symbol.
My other concern is that there are actually two allocations happening, but since new
is not underlined, then it is just the allocation from the method symbol (resulting in a delegate). I want to ensure/verify that only one allocation is happening in this scenario.
Again, I am new to all of this, so I appreciate your patience in explaining anything that I have mistaken here. Thanks again!
Possible allocation, except [NoEnumeration] parameters
I have a following results:
As you can see on my machine the "slow" delegates have exactly the same results as "normal".
However there we definetely can see 4 different time patterns:
My environment: Win 7, x64, .NET 4.5.2, Release, without debugger attached.
The results
1 => 00:00:00.0142181
2 => 00:00:00.0135044
3 => 00:00:00.0134526
4 => 00:00:00.0134764
5 => 00:00:00.0134741
6 => 00:00:00.0131344
7 => 00:00:00.0144817
8 => 00:00:00.0135706
9 => 00:00:00.0142359
10 => 00:00:00.0221315
11 => 00:00:00.0228191
12 => 00:00:00.0223610
13 => 00:00:00.0136163
14 => 00:00:00.0142727
15 => 00:00:00.0221889
16 => 00:00:00.0222276
17 => 00:00:00.1395306 // slow: from interface 'IBar' method
18 => 00:00:00.1352448 // slow: from interface 'IBar' method
19 => 00:00:00.1286564 // slow: from interface 'IBar' method
20 => 00:00:00.1372900 // slow: from interface 'IBar' method
21 => 00:00:00.1368202 // slow: from interface 'IBar' method
22 => 00:00:00.0134871 // slow: anonymous function in a generic method is generic itself (closed with int)
23 => 00:00:00.4364121 // slow: anonymous function in a generic method is generic itself (closed with string)
24 => 00:00:00.0130715 // slow: method group parametrized with type parameter 'T' of method 'GenericTest2' (closed with int)
25 => 00:00:00.4046351 // slow: method group parametrized with type parameter 'T' of method 'GenericTest2' (closed with string)
26 => 00:00:00.0131494 // slow: method group parametrized with type parameter 'T' of method 'GenericTest3' (closed with int)
27 => 00:00:00.4365968 // slow: method group parametrized with type parameter 'T' of method 'GenericTest3' (closed with string)
28 => 00:00:00.0143012
29 => 00:00:00.0159335
30 => 00:00:00.0131130
31 => 00:00:00.3981007
32 => 00:00:00.0132632
33 => 00:00:00.4432385
34 => 00:00:00.0137143
35 => 00:00:00.0157189
36 => 00:00:00.0123428
37 => 00:00:00.0124617
38 => 00:00:00.0123390
39 => 00:00:00.0123479
40 => 00:00:00.0133724
41 => 00:00:00.0134251
42 => 00:00:00.0131928
43 => 00:00:00.4510371
44 => 00:00:00.0144458
45 => 00:00:00.0175602
46 => 00:00:00.0131335
47 => 00:00:00.4492905
48 => 00:00:00.0144066
49 => 00:00:00.0171954
50 => 00:00:00.0119635
51 => 00:00:00.0119602
52 => 00:00:00.0135263
53 => 00:00:00.0133481
54 => 00:00:00.0130999
55 => 00:00:00.3992917
56 => 00:00:00.0131932
57 => 00:00:00.4093388
58 => 00:00:00.0123572
59 => 00:00:00.0123129
60 => 00:00:00.0123526
61 => 00:00:00.3986213
62 => 00:00:00.0131190
63 => 00:00:00.3941653
struct MyValType {
public override string ToString() {
return base.ToString();
}
}
As per these C# Design Meeting notes, the closure for local functions is (or can be) a struct, not a class:
But with functions, the delegate is never there (unless you explicitly decide to create one when you need it), and if the function itself is not captured as a delegate, the closure can be a struct on the stack.
However, all kinds of local functions I tried out were flagged as "Closure allocation".
Hi,
Normally, I treat these allocations pretty seriously, I stop to think if this is really required etc.
I definitely DO NOT care about this in unit-tests, even more so, specifically in Assert.That()
statements... Is there a way to control the visualizer's behavior?
It would be nice if there was the ability to ignore certain classes of object creation, for example anything inheriting Exception
or EventArgs
. Or if that isn't possible, then the ability to ignore names based on patterns, so I could build my own list of exclusions.
Perhaps the latter (or a combination of the two) might be preferable, as some people may wish to exclude exceptions whilst others want to exclude something that I might want to see.
Thanks for an interesting tool!
Hi,
Seems 0.92 doesn't highlight this method as allocating memory in heap.
I suppose that for reference types it allocates memory on heap, but for value types it performs boxing (as method returns System.Object).
I'll check deeper when i have profiler under my hand.
Hey there!
This plugin is a must have and sadly it was not updated for 2017.3 ReSharper, would be nice to see an update!
Because iterators have cached .GetEnumerator()
Verify allocation changes in C# 6.0 mode
I don't know all the cases in which calling a method having a params
parameter results in allocation, but at least some calls must do. I don't see any indication via this extension however.
void Foo(params object[] bar) { }
Foo(1); // shows boxing
Foo("Foo"); // no indication that object[] is allocated
Foo(); // does this allocate? probably not
Disable boxing highlighting inside string concatenation:
https://github.com/dotnet/roslyn/pull/415/files#r24541353
note: only enumeration and primitive types
From Twitter thread: https://twitter.com/pieceofsummer/status/922743962271670273
Pls :)
void M<T>(T val) /* unconstrained */ {
if (val != null) { }
if (val is int) { }
if (val is IEquatable<T>) { }
val?.ToString();
}
void M<T>(T val) where T : struct {
if (val is int) { }
if (val is IEquatable<T>) { }
}
var x = 42;
if (x is object o) { } // alloc
switch (x) {
case ValueType _: // alloc?
}
Big big fan of this project. :) I wish it was in R# by default... I basically went down a rabbit hole with my codebase recently... did not know that delegates were so expensive until I got into profiling with dotTrace. Did some investigating and found the JetBrains blog posts that led me to here. Totally bummed about my design decision now but I guess I will carry it with me going forward.
Anyways, the reason for this issue is about empty delegates:
() => {}
I've noticed these do not get hints. Are they not counted for some reason? Or possible bug?
Thanks again for your great work!
Member invocation on a readonly value type field causes a temporary copy of the field to be made before invocation.
From Mr Skeet:
http://msmvps.com/blogs/jon_skeet/archive/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields.aspx
This constant pattern produces boxing or two (depending on x
type):
x is 42
Since the generated code is:
object.Equals(42, x)
for (int i = 0; i < this.field.Length; i++) {
// ..
}
Hello,
Any plan on supporting new versions of Resharper? I was able to build a custom version for 2017.2 with only minor changes (using the Wave06 project), but I doubt all users will have enough free time for that.
Thanks.
(int, int) ii = (1, 2);
(object, object) oo = ii;
Need type conversion classification API to solve this :|
It would be nice to be able to differentiate any allocation of IDisposable object with a different color than other standard object.
Thanks a lot.
PS: I know It could be done through R# with the find pattern feature.
subj
For example, in boxing analyzer check the source expression type to be of value type first
It is a bit obvious, but on new XXX of course I'm allocating a new object, so I really don't want to be reminded that I'm allocating a new object on a new object creation :)
Thank you very much for this plugin!.
ReSharper source code has special struct collection types like LocalList<T>
that are supposed to be used only in current method, to prevent struct copy (it is mutable). With future collections like StackList<T>
(it stores first N element in N struct fields) it became more important to catch all the struct copyings and captures into closure (such struct will be 'huge').
I propose adding support for [StackOnly]
annotation and validation daemon to produce errors:
LocalList
)?ref
parameter"Sources of struct copying:
readonly
field, foreach
variable declaration, from
/join
/etc range variableout
), return value, operator operand, conditional expressionvoid Deconstruct(this int x, out int a, out int b) => a = b = x;
(object a, object b) = 42; // boxing 2x
(a, b) = 43; // boxing 2x
I am using VS2013, R# 2016.3.2 and when I write something like string.Format("{0}", somevalue) the brackets and the reference number are highlighted in green.
But if I install the heap allocation extension this behavior disappears.
using System;
public class C {
public void M(Action a) { }
public void M(out int x) {
for (;; M(out /*display class*/ var y), M(() => y++)) {
}
}
}
It would be cool to get insight on this from R# 2016.3 usage analyzer, since we need this 'locally invoked' knowledge for some features over local var
Still learning here. :)
Please read the following issue and attached images:
https://youtrack.jetbrains.com/issue/RSRP-459156
Part of the reason why I went down this path is that I did see that there was an underline hint in the created delegate, but there is also a boxing allocation which is marked as red, but hidden behind the hint (yellow). If you look REAL close, you can see the red, but not offhand.
If I saw the red first when going down this path, then I would have saved myself a lot of work... or learning. ๐ Would it be possible to ensure the red is displayed overtop the hint?
Please let me know if you have any questions.
dotnet/roslyn#6642 - this maybe will solve the method groups allocations issue
Hi,
Could you please explain this more precisely?
I've wrote the code:
IEnumerable a = Enumerable.Range(1, 100);
IEnumerable b = Enumerable.Range(1, 1000);
IEnumerable c = a.Concat(b);
The Concat call is highlighted. In Reflector i see
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable,
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable2)
L_0000: ldc.i4.1
L_0001: ldc.i4.s 100
L_0003: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, int32)
L_0008: stloc.0
L_0009: ldc.i4.1
L_000a: ldc.i4 0x3e8
L_000f: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, int32)
L_0014: stloc.1
L_0015: ldloc.0
L_0016: ldloc.1
L_0017: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Concat<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
L_001c: pop
L_001d: ret
}
There are no allocations, or do i miss something important ?
If this is possible with R# engine... Currently a simple generic static factory hides the allocation.
this
capture...string GetOrCreate(int key, Func<int, string> factory);
string GetOrCreate(int key, TState state, Func<TState, string> factory);
void M(int index, string captured) {
var text = smth.GetOrCreate(index, _ => captured);
//
var text = smth.GetOrCreate(index, c, (_, c) => c);
}
Anyone have a working R# 2018.1 version of this yet? Only thing keeping me from upgrading R#...can't live without this plugin!
This plugin messes up semantic syntax coloring, that is, this thing:
Here's how some code looks like without the plugin:
Notice the different colors for methods, classes, and structs.
Here's what the same code looks like with the plugin installed:
I'm using VS 15.8.2 with R# 2018.2.1.
IIRC this issue was not present with VS 15.7 and R# 2018.1.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.