This is from this issue: https://github.com/sveltejs/svelte/issues/2546
Why is the statement "surely the incremental cost of a component is significantly larger?" true?
Can someone unpack that for me?
Further to hackape's answer, here's a chart that I hope will illustrate the difference.
On the left, we have traditional frameworks that have a black-box approach. The components work like an instruction set for the framework. As a result, the framework usually includes all the functionality available, since it doesn't know what may be used. (caveat, some frameworks like Vue-3 now allow creating a bundle that only includes the parts needed). On the other hand Svelte will compile the components, and inject the parts needed into the component. The tipping point happens when the all the functionality added to each component surpasses the size of the framework (react, vue, etc). Given that the size of the injected svelte script is based on the contents of the component, it is hard to tell, based on number of components alone, when this tipping point may occur.
To be clear when Rich said “cost” he was referring to the bundle size of the compiled code. In web app’s context that’s obviously a cost. For illustration purpose, let’s compare svelte against react.
React needs both the 'react' runtime lib and the 'react-dom' renderer lib, aka the VDOM runtime, in order to work properly. So you pay the upfront cost for the size of these runtime libs. But for each component you add to you app’s bundle, the “incremental cost” in term of bundle size is just the size of component’s code, almost as is, if you don’t use JSX. Even if you do, the inflation rate of code size, from source code to compiled code, is close to 1.
With svelte, you don’t need a VDOM runtime. So there’s little upfront cost beside the tiny svelte runtime lib. But for each component you add, your source .svelte code will be compiled (and inevitably inflated) by the compiler into .js code, and the inflation rate is much greater than 1. Thus Rich said “ the incremental cost of a component is significantly larger”.
Some one has done the math. According to #halfnelson we have these two equations:
Svelte Bundle Bytes = 0.493 * Source_Size + 2811
React Bundle Bytes = 0.153 * Source_Size + 43503
His calculation is done using minified code so both multiplier is less than 1. You can tell the “incremental cost” per component of svelte is 3x the cost of react. But the upfront runtime cost is 1/15 of react’s.
Related
I'm building a simple compiler which emits WebAssembly. As I craft the Wasm that the compiler will emit, there are often multiple ways to implement a given behavior and I'm left unsure which one would be more performant.
For example, there are some cases where I could chain some math instructions to avoid storing/retrieving a value into/out of a variable. When is that tradeoff worth making? Is that even a thing I should be considering?
Obviously the only real answer to that question is "build both and then measure the performance on multiple Wasm interpreters", but that feels infeasible for the number of questions I have. I'm guessing there are some types of instructions which are an order of magnitude more expensive than others, and knowing that would help me make better intuitive decisions.
Are there any rules of thumb for how to think about this? Has anyone written about this? Are there tools which will show me what a given browser/interpreter would compile a snippet of Wasm to?
Sounds like you need something similar to JsPerf but for WASM.
The performance is going to depend on the browser and version you're using and I'm afraid there isn't a good answer here. If you take a look at js performance over time, compilers do help a bit but it is the vendors who can do something meaningful.
Over time we can expect the gap between wasm and native apps to short quite a bit and I suspect the main bottleneck for wasm is the bridge between js and wasm.
There is a paper that may interest you: https://www.usenix.org/system/files/atc19-jangda.pdf
You can optimize you Wasm code to reduce the size of the output. Indeed, a smaller output file can be downloaded faster. This is important if your Wasm code is stored on a web server and executed in client browsers.
Beside this, the Wasm compiler in the browser could optimize the code too. But even in such case, it is not totally pointless to do the optimizations ahead of time in your compiler because the browser putting more burden to the browser will likely often cause it to compile the code to a native binary more slowly. This results in slightly bigger loading times or slightly less responsive web applications.
I'm writing my first complex Redux/React SPA and I expect to end up with at least 500 components.
I was told that I should merge some components because the less components the faster the app will be (I guess that's the rendering issue?). On the other hand everywhere I go (like official docs) it says the components should be as small as possible.
Which approach is best?
I think, you should read this article https://medium.com/dailyjs/react-is-slow-react-is-fast-optimizing-react-apps-in-practice-394176a11fba
The main steps for improving your application:
1) small reusable components
2) using shouldComponentUpdate
3) using PureComponent
Big components is the wrong way to structure your application, because react.js spends more time to render screen
All DirectX books and tutorials strongly recommend reducing resource allocations between draw calls to a minimum – yet I can’t find any guidelines that get more into details. Reviewing a lot of sample code found in the web, I have concluded that programmers have completely different coding principles regarding this subject. Some even set and unset
VS/PS
VS/PS ResourceViews
RasterizerStage
DepthStencilState
PrimitiveTopology
...
before and after every draw call (although the setup remains unchanged), and others don’t.
I guess that's a bit overdone...
From my own experiments I have found that the only resources I have to bind on every draw call are the ShaderResourceViews (to VS and PS in my case). This requirement may be caused by the use of compute shaders since I bind/unbind UAVs to buffers that are bound to VS / PS later on.
I have lost many hours of work before I detected that this rebinding was necessary. And I guess that many coders aren’t sure either and prefer to unbind and rebind a “little too much” instead of running into a similar trap.
Question 1: Are there at least some rules of thumb regarding this problem?
Question 2: Is it possible that my ShaderResourceViews bound to VS/PS are unbound by the driver/DirectX core because I bind UAVs to the same buffers before the CS dispatch call (I don’t unbind the SRVs myself)?
Question 3: I don't even set VS/PS to null before I use the compute shaders. Works w/o problems yet I feel constantly unsure whether or not I'm digging my next trap using such a "lazy" approach.
You want to have as less overhead, but also while avoiding invalid pipeline state. That's why some people unbind everything (try to prevent as much), it depends on uses cases, and of course you can balance this a bit.
To balance this you can pre allocate a specific resource to a slot, depending on resource type, since you have a different number of slots, different rules can apply
1/Samplers and States
You have 16 slots, and generally 4-5 samplers you use 90% of the time (linear/point/anisotropic/shadow).
So on application startup create those states and bind them to each shader stage you need (try not to start at zero slot, since they would easily be overriden by mistake).
Create a shader header file with mapping SamplerState -> slot, and use it in your shaders, so any slot update is reflected automatically.
Reuse this as much as possible, and only bind custom samplers.
For standard states (Blend/Depth/Rasterizer), building a small collection of common states on application startup and bind as needed is common practice.
An easy way to minimize Render State binding at low cost, you can build a stack, so you set a default state, and if a shader needs a more specific state, it can push new state to the stack, once it's done, pop last state and apply it again to the pipeline.
2/Constant Buffers
You have 14 slots, which is quite a lot, it's pretty rare (at least in my use cases) to use all of them, specially now you can also use buffers/structuredbuffers as well.
One simple common case is setting a reserved slots for camera (with all data that you need, view/projection/viewprojection, plus their inverses since you might need that too.
Bind it to (all if needed) shader stage slots, and only thing you have to do is update your cbuffer every frame, it's ready to use anywhere.
3/Shader stages
You pretty much never need to unbind Compute Shader, since it's fully separated from the pipeline.
On the other side, for pipeline stage, instead of unbinding, a reasonably good practice is to set all the ones you need and set to null the ones you don't.
If you don't follow this as example and render a shadow map (depth buffer only), a pixel shader might still be bound.
If you forget to unset a Geometry Shader that you previously used, you might end up with invalid layout combination and your object will not render (error will only show up in runtime debug mode).
So setting the full shader stage adds little overhead, but the safety trade off is very far from negligible.
In your use case (using only VS/PS and CS to build), you can safely ignore that.
4/Uavs-RenderTargets-DepthStencil
For write resources, always unset when you done with unit of work. Within the same routine you can optimize inside, but at the end of your render/compute shader function, set your output back to null, since pipeline will not allow anything to be rebound as ShaderResource while it's on output.
Not unsetting a write resource at the end of your function is recipe for disaster.
5/ShaderResourceView
This is very situational, but idea is to minimize while also avoiding runtime warnings (which can be harmless, but then hide important messages).
One eventual thing is to reset to null all shader resource inputs at the beginning of the frame, to avoid a buffer still bound in VS to be set as UAV in CS for example, this costs you 6 pipeline calls per frame, but it's generally worth it.
If you have enough spare registers and some constant resources you can also of course set those in some reserved slots and bind those once and for all.
6/IA related resources
For this one, you need to set the right data to draw your geometry, so everytime you bind it it's pretty reasonable to set InputLayout/Topology . You can of course organize your draw calls to minimize switches.
I find Topology to be rather critical to be set properly, since invalid topology (for example, using Triangle List with a pipeline including tesselation), will draw nothing and give you a runtime warning, but it's very common that on AMD card it will just crash your driver, so better to avoid that as it becomes rather hard to debug.
Generally never really unbinding vertex/index buffers (since just overwriting them and Input layout tells how to fetch anyway).
Only exception to this rule if in the case those buffers are generated in compute/stream out, to avoid the above mentioned runtime warning.
Answer 1 : less is better.
Answer 2 : it is the opposite, you have to unbind a view before you bind the resource with a different kind of view. You should enable the debug layer to catch errors like this.
Answer 3 : that's fine.
I've inherited a Visual Studio Solution that contains numerous circular references between Projects.
Is there ever a situation where this is remotely acceptable?
Just trying to confirm my suspicion that this application is designed horribly. Thanks in advance.
I once read a column where they compared 3 kinds of models: the Spaghetti model, the Lasagna model and the Ravioli model.
In the Spaghetti model, all the code is interlinked with each other, there is no clear structure. It's horrible, and we probably can all agree on that.
In the Lasagna model, the code is divided in different layers, and only a higher-level layer can access a lower-level layer, never the other way around.
In the Ravioli model, code is grouped in smaller modules. Every module only exposes what needs to be exposed, but every module can still access every other module.
About 10 years ago, it seemed to me that the Ravioli model is better than the Lasagna model. After all, in Java you also have Java modules which can easily call each other (and I had the impression that there was no real structure between all the different Java modules). To me, the Lasagna model seemed the result of non-object-oriented old code, while the Ravioli model seemed more modern, more object-oriented.
Nowadays, I tend to go back to the Lasagna model, but with a Ravioli model built-in. This is:
The application is built using different layers, like in the Lasagna model
But within the layers, the code is still split up in between different modules that can access each other, like in a Ravioli model.
Certain circular references may be difficult or impossible to remove. An example is the following:
Suppose you have a FileWriter class in your application and a Debug class. The Debug class will need the FileWriter class since it needs to write files with debug information.
On the other hand, the FileWriter class may also want to use the Debug class.
Notice that the circular reference in this example may already lead to problems (the FileWriter class could call the Debug class while writing a line, but the Debug class uses the FileWriter class to write the debug information, result: stack overflow).
In this case, the problem could be easily solve by not using the FileWriter class in the Debug class, but to use the native iostreams (if you're developing in C++). In other cases, the problem might be much harder to solve.
Good software is designed in layers with clearly demarcated boundaries between them. ie: If you have a layer, you need to be able to clearly articulate what it does, why it's there, and what it depends upon. Circularities make this difficult to achieve, and should generally be removed. (Microsoft has spent lots of effort in Windows 7 to improve the layering of Windows, by removing Circularities.)
Just trying to confirm my suspicion that this application is designed horribly.
This would definitely support that theory, but IMO, you'd need more than just a few circular references to draw that conclusion.
To answer your original question: yes, circular references can be helpful at times. Mutually recursive functions are a good example of that kind of thing. However... that's a circular reference safely hidden away within a module. For inter-module dependancies, a circular dependency usually means that your code isn't correctly split out across the modules, and this can require some significant refactoring to fix. (Including adding new sorts of abstactions to bridge the gap, etc.)
Concretely I'd advise using NDepend to detect and avoid dependency cycles.
Excerpt from the article (I wrote): Control component dependencies to gain clean architecture
Dependency cycles between components lead to what is commonly called spaghetti code or tangled code. If component A depends on B that depends on C that depends on A, the component A can’t be developed and tested independently of B and C. A, B and C form an indivisible unit, a kind of super-component. This super-component has a higher cost than the sum of the cost over A, B and C because of the diseconomy of scale phenomenon (well documented in Software Estimation: Demystifying the Black Art by Steve McConnell). Basically, this holds that the cost of developing an indivisible piece of code increases exponentially.
This suggests that developing and maintaining 1,000 LOC (Lines Of Code) will likely cost three or four times more than developing and maintaining 500 LOC, unless it can be split in two independent lumps of 500 LOC each. Hence the comparison with spaghetti that describes tangled code that can’t be maintained. In order to rationalize architecture, one must ensure that there are no dependency cycles between components, but also check that the size of each component is acceptable (500 to 1000 LOC).
Circular project references are a sign of bad design and should be removed if at all possible.
The only justification I can think of for keeping a circular reference would be a back compat issue. Even then it seems like it could be fixed with the use of type forwarders and another assembly
What is the preferred way to build application gui components tree?
Instantiate all components and build an entire tree, controlling it with show/hide/disable/enable operations on user events.
Dynamically creating gui with create/add/remove components based on user events.
I'm especially interested with this design problem in JavaFX.
Sorry, I don't know much about JavaFX.
But, I would suggest option 2. If you instantiate everything at the start, you're going to use up a whole load of memory when you only actually need to use memory for the gui components that are currently visible.
Create all of the components for the current screen, and show/hide/disable/enable them. But don't create components that don't live on the current screen/window/form/dialog.
The answer depends mainly on performance. I have built trees with ~3000 nodes with no problem. At some point in time the number of nodes added to the Scene does impact performance, but this is a moving target as each release of JavaFX is improving on this.
However, not all of this performance degradation is due to the number of nodes as it may be due to "BindStorming". See Jim Connors blog on this and other performance related posts.