6 ms·
v8 (before v5.9) used to only inline functions that were under 600 characters (and 196 AST nodes). That was another fun way to add fuel to the tab vs. spaces fi
by zbjornson 8y ago
v8 (before v5.9) used to only inline functions that were under 600 characters (and 196 AST nodes). That was another fun way to add fuel to the tab vs. spaces fire: identical functions that used spaces instead of tabs could run significantly slower because they weren't inlined.
- umvi 8y ago> The kernel compiler, GCC, is unaware to the code size that will be generated by the inline assembly. It therefore tries to estimate its size based on newline characters and statement separators (';' on x86). Seems like a poor decision to use whitespace as a factor in estimating code size. Some people like to space out their code with lots of comments even if it is only a few lines long. This is my naive view, though - does anyone out there have any insight as to why semicolons alone aren't sufficient to estimate code size?
- chrisseaton 8y ago> Seems like a poor decision to use whitespace as a factor in estimating code size. It's a heuristic. It's always going to be more poor in some respect that doing something precise. You could probably make it better in a million different ways, but it's suppose to be quick and simple.
- trhway 8y agoto inline the parsing must be done. Once parsing is done there is no newlines and semicolons, only AST nodes. Counting AST nodes is very quick and simple and provides for much better heuristic.
- chrisseaton 8y ago> to inline the parsing must be done Yes... that's the point. For your metric counting AST nodes you need to have parsed the method. The other heuristic works on unparsed source-code. So you don't waste time parsing a method that you won't inline. > Counting AST nodes is very quick and simple But parsing is neither. And you need to have done that first. I think (there's a paper I'm sure but can't find it) that parse time for JS applications is very substantial.
- MikeHolman 8y agoYou don't want to inline functions that haven't been parsed. If it hasn't been called by the time you are JITing, then it probably isn't going to be on a hot path.
- chrisseaton 8y agoIf it's been JITed then the AST may well have been discarded after the JIT completed! Doesn't V8 work like that today? You'll need to pass it again even if you have already parsed it! It's not hard to think of worst-cases. Think about an enormous function that you would not want to inline. It's been JITed, or it's running in a byte code interpreter, and the AST has been discarded. In your scheme you'll have to parse it to find it's too big. Huge waste of time, if you can see it's too large from the source code.
- MikeHolman 8y agoYou can't throw away your interpreter bytecode, because the JIT code needs to be able to bailout if a guard fails (e.g. if you specialized a var as an int and you get a non-int). So you can always use the size of your bytecode as an inlining heuristic (which is what Chakra does). Of course that wouldn't work before v8 had an interpreter, but they could have used other heuristics. For example, they could have used the size of the AST as a heuristic and saved that even after the AST gets thrown away.
- noselasd 8y ago> the parsing must be done. Therein lies the bulk of the problem, gcc doesn't parse the assembly code. The assembly is just copied with some minor string replacements for the registry names, and it's up to the assembler to deal with it and parse it.
- improbable22 8y agoEven with a simple heuristic, wouldn't you try to pick this magic number 600 so that it's about as likely to be too big as too small? Or is the problem very asymmetric, that inlining too-large pieces has dramatic downsides that failing to inline small pieces does not? Or is it just that the magic number was chosen decades ago, and the tradeoffs (perhaps re memory) are different now?
- chrisseaton 8y ago> wouldn't you try to pick this magic number 600... I guess ideally you'd run a sophisticated machine learning algorithm across a huge corpus of real-world code, on real-world devices to discover the right number... but in reality people just don't have time for that so they pick a number and move on. Can't really blame them.
- improbable22 8y agoI had a binary search in mind, if that counts as sophisticated :) Just surprised by orig GGP's assertion that more inlining would automatically be faster.
- IshKebab 8y agoRead the article - LLVM does it correctly.
- deleted 8y ago[deleted]
- dan-robertson 8y agoYou don’t put semicolons between instructions in assembly. A semicolon is used for comments to the end of the line. Instructions go one per line
- comex 8y agoNo, the GNU assembler (unlike some others) does use semicolons as an instruction separator. Comment markers include // and // (like C), as well as potentially #, @, or others depending on the architecture. Edit: Actually, on some lesser-used architectures it does support using ; to start a comment, e.g. z80 [1], but not the most common ones. [1] https://sourceware.org/binutils/docs-2.26/as/Z80_002dChars.html#Z80_002dChars https://sourceware.org/binutils/docs-2.26/as/Z80_002dChars.h...
- jcranmer 8y agoThis is measuring code size in inline assembly. The assumption underlying the heuristic is that code size is proportional to the number of assembly instructions and that a line of inline assembly is usually an instruction (although a separator may mean multiple instructions per line). It is not looking at the C code itself, only the inline assembly string. That heuristic is actually usually a very good one. However, in this case, the inline assembly contains a lot of computation in directives, so it consists of several lines to compute one small instruction. To answer the question you actually asked rather than what you meant to ask (because the question you asked is interesting in and of itself): compilers throw away the actual source code very quickly. At the stage that inlining is considered, the program is going to consist of a sequence of instructions not unlike the C abstract machine as opposed to a more direct representation of code. The simplest way to estimate a program size is to simply count these instructions, but some instructions stand a good chance of being eliminated (say, a bitcast instruction that changes type without adjusting value), so there's usually some adjustment for "this is really expensive on this machine" or "this instruction is free."
- mjevans 8y agoArguably inlining (or not) should be deferred to the link / output phases and there would be enough information present to perform the operation or not when putting the puzzle of segments together.
- dagenix 8y agoThe problem with doing this is that many other optimizations depend on inlining. So, if inlining is deferred until link time, thise optimizations will be far less useful. Take for example auto-vecorization. If we have a loop that just calls a function which increments a value in an array, we may very well have a great candidate to vectorize. However, if the function isn't inlined, the auto vectorization optimization will just see a function call as opposed to an increment and won't be able to do anything with it.
- vlovich123 8y ago