git ignore:

This commit is contained in:
Simon Kellet
2026-03-07 16:31:25 +00:00
parent 6f3e0899bb
commit 1ffc88f530
21 changed files with 990 additions and 135 deletions
BIN
View File
Binary file not shown.
+48 -17
View File
@@ -1,38 +1,51 @@
# CSCU9A5 Week 1
## Read Chapters 1 and 2 of Clean Code
>
> You get the drift. Indeed, the ratio of time spent reading vs. writing is well over 10:1. We are constantly reading old code as part of the effort to write new code. Because this ratio is so high, we want the reading of code to be easy, even if it makes the writing harder. Of course theres no way to write code without reading it, so making it easy to read actually makes it easier to write.
### Class names
>
> Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager, Processor, Data, or Info in the name of a class. A class name should not be a verb.
### Method names
>
> Methods should have verb or verb phrase names like postPayment, deletePage, or save. Accessors, mutators, and predicates should be named for their value and prefixed with get, set, and is according to the javabean standard.
```java
string name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())...
```
> When constructors are overloaded, use static factory methods with names that describe the arguments. For example,
```java
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
```
> is generally better than
```java
Complex fulcrumPoint = new Complex(23.0);
```
> Consider enforcing their use by making the corresponding constructors private.
### Note: Listings 2-1 and his solution in 2-2 make me cringe
Its too long! Random methods with very long names makes it hard to read on screens like IDE's. You also end up jumping around the code, back and forth and trying to figure out what method returns what and where the next bit of code goes. There is also the issue of creating entire methods for one case of something happening. You could easily split the decision making within the one method with better coding. You can check if there is more and 0 counts and then handle what happens there. We already have access to all the variables we need passed as parameters, why split this off into many methods. I hate it.
## Extended BNF (EBNF)
* We can use * to make things **appear 0-to-many times** by wrapping then in brackets
```text
N ::== a | (b | c)*
```
This can be read as **"N may consist of a or alternatively a series of characters comprising zero to many b or c"**. A valid syntax matching this rule would include:
* "a"
* an empty string (the * helps with this as its 0 to many)
* "b"
@@ -43,7 +56,9 @@ This can be read as **"N may consist of a or alternatively a series of character
* or "ccccccb"
## Example
We have a simple programming language that allows the user to write arithmetic sums for some existing variables **a, b, c, d, or e**. We can use +, -, /, * and () on the variables too.
```text
Expression ::== primary-Expression (Operator primary-Expression)*
primary-Expression ::== Identifier
@@ -51,7 +66,9 @@ primary-Expression ::== Identifier
Idenifier ::== a | b | c | d | e
Operator ::== + | - | / | *
```
For example:
```text
a + (b * c)
```
@@ -60,16 +77,16 @@ Here you can view the [Java](https://docs.oracle.com/javase/specs/jls/se18/html/
## Triangle
## Features of Triangle:
## Features of Triangle
* Three primitive types of variables:
* Boolean (i.e. true or false)
* Char (a single character; same as the Java char type)
* Integer (values between -32767 and 32767)
* Boolean (i.e. true or false)
* Char (a single character; same as the Java char type)
* Integer (values between -32767 and 32767)
* Two types of composite variable:
* Records (a bit like dictionaries in python)
* Arrays
* Records (a bit like dictionaries in python)
* Arrays
* There are no strings or floating point types built-in
@@ -78,28 +95,30 @@ Here you can view the [Java](https://docs.oracle.com/javase/specs/jls/se18/html/
* The only conditional command is **if** and the only loop command is **while**
* **if** commands look like this:
* if x < y then *dostuff* else *dootherstuff*
* the “else” is needed, but can just be followed by ; (do nothing)
* if x < y then *dostuff* else *dootherstuff*
* the “else” is needed, but can just be followed by ; (do nothing)
* **while** commands look like this:
* while x < y do *dostuff*
* while x < y do *dostuff*
* **begin** and **end** signify a block of commands; the same as {} in Java or indentation in Python
* Assignment of a value to a variable is done using the := operator like this
* a := 10
*
* a := 10
*
* We can also bind values within declarations by using **~**
* **put()** and **get()**, and similarly named functions, are used to write and read from the console
### Examples of Triangle (.tri)
```
#hi.tri
begin
put('H'); put('i'); put('!')
end
```
Output: **Hi!**
```
@@ -116,6 +135,7 @@ begin
end
end
```
Output: **aaaaa**
```
@@ -134,13 +154,14 @@ begin
put (s[0]); put(s[9]); puteol()
end
```
Output: **
## AST Examples
***See OneNote folder (Year\ 3/AST/) for the answers***
## Syntactic Analysis
## Syntactic Analysis
Let's have a look at Java's syntax
@@ -148,10 +169,11 @@ Let's have a look at Java's syntax
int myNumber = 55;
```
**Kind*** is the category (Identifier, Integer etc)
**Kind***is the category (Identifier, Integer etc)
**Spelling*** is the actual text used in the code
Let's break it down into tokens:
Let's break it down into tokens:
* **int** - of kind Identifier(name of type) (spelling "int")
* **myNumber** - of kind Identifier(name of variable) (spelling "myNumber")
* **=** - of kind Becomes (spelling "=")
@@ -168,7 +190,8 @@ in
```
Let's break it down into tokens again:
* **let, :, var, in** of kind let, :, var, in (spelling "let, :, var, in ")
* **let, :, var, in** of kind let, :, var, in (spelling "let, :, var, in ")
* **:=** of kind Becomes (spelling "Becomes")
* **myNumber** of kind Identifier (spelling "myNumber")
* **Integer** of kind Identifier (spelling "Integer")
@@ -182,6 +205,7 @@ The basic ideas is that we have a loop implemented using recursion that gobbles
myNumber := 55+ 10
^
```
Our starting point is char 'm', so we assume that we are working along a Identifier. We continue working our way through to the end of the word (until we hit a space). At this stage, it is impossible to know if we are working with either method names, string literals or anything of that nature.
```
@@ -224,7 +248,8 @@ myNumber := 55+ 10
~~~~~~~~~~~~~~^
```
The next character is a plus. Operators can start with this character so we keep taking characters until reaching something that's not an operator. The next character is a space
The next character is a plus. Operators can start with this character so we keep taking characters until reaching something that's not an operator. The next character is a space
```
myNumber := 55+ 10
~~~~~~~~~~~~~~~~~~^
@@ -252,6 +277,7 @@ Verb ::== like | is | see | sees
|
the cat sees a rat.
```
```
Subject
____
@@ -259,6 +285,7 @@ Subject
| |
the cat sees a rat.
```
```
S
____
@@ -266,6 +293,7 @@ the cat sees a rat.
| | |
the cat sees a rat.
```
```
S
____
@@ -273,6 +301,7 @@ the cat sees a rat.
| | | |
the cat sees a rat.
```
```
S Obj
____ ____
@@ -280,6 +309,7 @@ the cat sees a rat.
| | | | |
the cat sees a rat.
```
```
Sentence
___________________
@@ -294,6 +324,7 @@ the cat sees a rat.
---
### Key definitions for compilers
* **Syntactic analysis**: scanning and parsing, which takes the text of the source code and transforms it into an abstract syntax tree
* **Contextual analysis**: checks things like variable types and scope, and creates the connections within the AST so we can later look up declarations for named identifiers like variables, constants and function.
+22 -18
View File
@@ -1,4 +1,4 @@
# CSCU9A5 Week 2
# CSCU9A5 Week 2
TODO
> Read Chapter 3 of Clean Code
@@ -22,20 +22,21 @@ Here there is two variables with the name **count**. How does the compiler know
We could walk back up the abstract syntax tree to figure out what needs to go where in memory (size, type etc.). That would work but, it's slow. (Think back to Tree Walks).
The ID table might contain a list of these *attributes* or just a *pointer* to the place as to where the identifier was declared.
The ID table might contain a list of these *attributes* or just a *pointer* to the place as to where the identifier was declared.
Declarations are functions, class in Java or even int etc.
Declarations are functions, class in Java or even int etc.
A *block* is any part of the program that limits the scope of the declaration in Java.
Each declaration has a **scope**.
Each declaration has a **scope**.
### Monolithic Block Structure
A programming language exhibits a monolithic block structure if there is only one block:
* All declarations are **global in scope**
* No identifier may be declared more than once
* For every reference to an identifier, *i*, there must be a corresponding declaration of *i*.
* For every reference to an identifier, *i*, there must be a corresponding declaration of *i*.
```code
program
@@ -51,7 +52,7 @@ One block for the **whole** program. The ID table may look like this:
|----------|-----------|
| b | (1) |
| n | (2) |
| c | (3) |
| c | (3) |
```code
program
@@ -69,15 +70,16 @@ end
### Flat Block Structure
Several overlapping blocks, local and global scopes.
Several overlapping blocks, local and global scopes.
### Nested Block Structure
Most programming languages fall into this structure (Java, C and Python).
Many scope levels, declarations can be global (scope level 1, outer most scope) in scope or local in scope.
Many scope levels, declarations can be global (scope level 1, outer most scope) in scope or local in scope.
Example:
```code
let
(1) var a : Integer
@@ -116,7 +118,7 @@ end
What do we need to store instead of the identifiers?
## Type Checking
## Type Checking
This is a key feature in a **statically** typed language like Triangle or Java. This helps programmers not make mistakes (assigning a string to an int). This type checking happens at compile time.
@@ -134,7 +136,7 @@ All values of a given type should occupy the same amount of space, this being th
The compiler then can efficiently work out where to map the values in memory as it knows all the space needed.
Should values be represented directly or indirectly?
Should values be represented directly or indirectly?
If a variable is directly represented, it simply maps to a binary representation of the variables value somewhere in heap memory.
@@ -150,7 +152,6 @@ If a variable is directly represented, it simply maps to a binary representation
If a value is indirectly, the variable maps to a **handle**, a pointer to a storage area where the binary representation of that variable exists (most likely in a heap memory area).
```text
| Address | Value |
|---------|-------|
@@ -179,12 +180,13 @@ Composite types are types which can be simplified into collections of primitive
### Record
**Record**: A collection of variables of fields, each of which has an identifier.
**Record**: A collection of variables of fields, each of which has an identifier.
* Records in Triangle
* Structs in C, Go etc.
* Java Class with only public variables (closest anyway...)
Simplest way to store this in memory is to store the variables consecutively in memory (like in a row).
Simplest way to store this in memory is to store the variables consecutively in memory (like in a row).
```code
type Date = record
@@ -201,6 +203,7 @@ type Details = record
```
This could be stored in memory like so:
```text
var person : Details
@@ -216,7 +219,6 @@ var person : Details
```
### Array
**Array**: consists of several elements which are all of the same type. Each element is referenced by an index (often an integer), there is a one-to-one relationship between indexes and array elements. Arrays start at 0 (some terrible languages don't)
@@ -251,7 +253,9 @@ Accessing the elements of this array requires an extra computation at runtime co
To summarise, broadly there are two approaches to storing temporary values: registers and stack. Registers are fixed locations that can be referenced directly, but this means a tricky process of choosing which registers to hold which values. Stacks grow and shrink to accommodate new values, but lead to simpler evaluation of expressions.
---
## Summary of Key Concepts
* Identification Table: associates identifiers with a list of attributes, or the original declaration
* Declaration: something like a function declaration in python, class declaration in Java, or a variable declaration like “int a;” in Java
@@ -261,13 +265,13 @@ To summarise, broadly there are two approaches to storing temporary values: regi
* Block: any part of the program that limits the scope of a declaration (e.g. curly brackets in Java, indentation in Python) In Triangle, scope is determined by the let...in... Command.
* Block structure: there are three types of block structure: monolithic, flat, and nested
* Non-confusion: different values of a given type should have different representations
* Uniqueness: each value should always have the same representation.
Two issues to remember in practice:
* All values of a given type should occupy the same amount of space
* Should values be represented directly or indirectly? If all values of a given type occupy the same space (that is, the same number of bits or bytes), it is possible for the compiler to plan the allocation of space efficiently simply by knowing the type of each variable.
+32 -48
View File
@@ -9,27 +9,30 @@ By the end of this week you should have the ability to:
* **Implement** simple additions to the visitor pattern within a compiler
* **Reflect** on the value of where and when good commenting is needed
## Runtime Organisation: Static Storage Allocation
## Runtime Organisation: Static Storage Allocation
Memory is basically like a very long list with each element having an address and a value.
If we have a variable that might change in size, and indirect representation is used - a pointer. This mean that the variable can occupy a fixed size space in memory because it just *points* to the address of where the data is really stored (usually in the **heap**).
### Stack storage allocation
### Stack storage allocation
> Stacks are last-in-first-out data structures: think of a stack of paper where you can only add or remove from the top. Stacks are an effective way to store local variables throughout the lifetime of a program.
***Stack Storage Allocation:***
* Variables are stored in frames; each frame contains the local variables for a routine
* Global variables are stored at the base of the stack
* Link data at the start of each frame contains:
* Static link (reference to the start of the frame of the routine containing the current one)
* Dynamic link (reference to the start of the frame for the previously active routine)
* Return address (reference to the code instruction to jump back to when the routine is finished)
* Static link (reference to the start of the frame of the routine containing the current one)
* Dynamic link (reference to the start of the frame for the previously active routine)
* Return address (reference to the code instruction to jump back to when the routine is finished)
* When a routine is called, a new frame is pushed onto the stack; when it returns, the frame is removed
* Arguments are placed on the stack immediately before a routine is called
* When a routine returns, its arguments and frame are replaced by a return value on the stack
Example of Routines and returns
```text
let
var g: Integer;
@@ -42,6 +45,7 @@ in
putint(F(g, g+1))
end
```
```asm
PUSH 1
LOADA 0[SB]
@@ -64,9 +68,9 @@ F:
### Heap Storage Allocation and Garbage Collection
> Heap storage allocation is another way to organise memory, and is good for indirect storage of variables.
> Heap storage allocation is another way to organise memory, and is good for indirect storage of variables.
A heap variable is allocated by a special command called an allocator in Java. This is whenever you use the keyword *new* and in C it is *malloc()*. These return a pointer to the variable in the heap. They exist in memory until it is unallocated (via *free()* in C or automatically in Java).
A heap variable is allocated by a special command called an allocator in Java. This is whenever you use the keyword *new* and in C it is *malloc()*. These return a pointer to the variable in the heap. They exist in memory until it is unallocated (via *free()* in C or automatically in Java).
Heap is placed in opposite to the stack:
@@ -83,13 +87,14 @@ Heap is placed in opposite to the stack:
```
***Heap Storage Allocation:***
* Heaps can indirectly store more complex data structures
* Heap variables are added to the heap when they are created
* They are either removed by explicit deallocation, or automatically by a garbage collector
* Gaps appear in the heap over time; these can be managed by:
* Trying to match new variables to the closest size of available gap
* Merging gaps when variables are deallocated
* Compacting the heap periodically
* Trying to match new variables to the closest size of available gap
* Merging gaps when variables are deallocated
* Compacting the heap periodically
* Garbage is when a variable is inaccessible, because no pointers to it remain in the program
* Explicit deallocation can lead to garbage or dangling pointers
* Automatic deallocation runs periodically, deallocating inaccessible variables
@@ -128,38 +133,39 @@ SUCC, means successor instruction to increase the value on the top of the stack
The Triangle Abstract Machine (TAM) is a virtual machine designed as the target for our case study compiler. It is also implemented in Java, and you have a copy of it by virtue of cloning the Triangle-Tools project. TAM has the following features:
- Its memory is organised to have a stack at the low-address end and a heap at the high address end. Low level operations are provided for adding and removing data in these
* Its memory is organised to have a stack at the low-address end and a heap at the high address end. Low level operations are provided for adding and removing data in these
- Call and return instructions for routines handle frames automatically; **return** automatically replaces the arguments in the stack with a result from the functions return.
* Call and return instructions for routines handle frames automatically; **return** automatically replaces the arguments in the stack with a result from the functions return.
- The only registers are dedicated to specific purposes as weve described. SB, ST, HB and HT to locate the stack and heap; LB points to the topmost frame on the stack, and so on. These are updated automatically by the instructions that add or remove things from memory
* The only registers are dedicated to specific purposes as weve described. SB, ST, HB and HT to locate the stack and heap; LB points to the topmost frame on the stack, and so on. These are updated automatically by the instructions that add or remove things from memory
- Several routines such as ADD, MULT, and NOT are provided for basic arithmetic and logic operations. There are also routines for reading and writing text on the console.
* Several routines such as ADD, MULT, and NOT are provided for basic arithmetic and logic operations. There are also routines for reading and writing text on the console.
A full description of TAM is given in the extracts from Programming Language Processors in Java given in the Canvas Reading List. You dont need to become familiar with this but the text is there to serve as reference material and you will likely want to refer to it when reading and extending the compiler.
### From Programming Language Processors in Java: Compilers and Interpreters by Watt, D.A.
### From Programming Language Processors in Java: Compilers and Interpreters by Watt, D.A
Yarr link be here!
<https://www.cin.ufpe.br/~jml/programming-language-processors-in-java-compilers-and-interpreters.9780130257864.25356.pdf>
- Both stack and heap can expand and contrast. Storage exhaustion arises when ST and HT attempt to cross over.
* Both stack and heap can expand and contrast. Storage exhaustion arises when ST and HT attempt to cross over.
#### Layout of a TAM frame
- A *static link* points to an underlying frame associated with teh routine that textually encloses R in the source program
- The *dynamic link* points to the frame immediately underlying this one in the stack.
- The *return address* is the address of the instruction immediately following the call instruction that activated R.
* A *static link* points to an underlying frame associated with teh routine that textually encloses R in the source program
* The *dynamic link* points to the frame immediately underlying this one in the stack.
* The *return address* is the address of the instruction immediately following the call instruction that activated R.
![2023-09-28_42](../media/2023-09-28_42.png)
![2023-09-28_42](../media/2023-09-28_42.png)
#### TAM instruction format
#### TAM instruction format
All TAM instructions have a common format.
- *op*: the operation code [4bits]
- *r*: a register number [4bits]
- *n*: the size of the operand. [8bits]
- *d*: address displacement (possibly negative) [16bit *signed*]
All TAM instructions have a common format.
* *op*: the operation code [4bits]
* *r*: a register number [4bits]
* *n*: the size of the operand. [8bits]
* *d*: address displacement (possibly negative) [16bit *signed*]
```text
[ op ][ r ][ n ][ d ]
@@ -169,25 +175,3 @@ All TAM instructions have a common format.
#### TAM instructions
![2023-09-28_55](../media/2023-09-28_55.png)
+26 -27
View File
@@ -1,6 +1,5 @@
# CSCU9A5 Week 4
## Read: Chapter 5 of Clean Code
> Variable declarations: Martin suggests that declarations should be as close to the usage of the variables as possible. Others feel they should all be at the top, or at least together somewhere. What's your opinion?
@@ -17,7 +16,7 @@ Method usage **local** are the ones you would declare at the start of a function
Sometimes it's helpful to declare all the variables at the start - this helps give context to what the function will be doing and possibly what it is returning without the need of comments. I find this helpful for functions that handle complex maths equations.
The variables used within loops should come before the loops start; this gives me as a programmer better control on the scope and also context. An example could be line number, character position and file name before a for loop. I know that these three variables will be used in this for loop for keeping track of where we are in the loop.
The variables used within loops should come before the loops start; this gives me as a programmer better control on the scope and also context. An example could be line number, character position and file name before a for loop. I know that these three variables will be used in this for loop for keeping track of where we are in the loop.
> Do you have any of your own formatting rules you follow (even if only sometimes?)
@@ -29,9 +28,9 @@ The **Visitor Pattern** is used to generate the necessary low level instructions
The algorithm we are using follows the **visitor pattern**. We can already see this in the visualisation of the AST.
We specifically write *visitNode* methods to view the tree.
We specifically write *visitNode* methods to view the tree.
The Visitor pattern walks the AST calling **emit()** methods to generate machine code instructions as it goes. Lookups into the AST are used to decide things like the value of literals or specific operators to use.
The Visitor pattern walks the AST calling **emit()** methods to generate machine code instructions as it goes. Lookups into the AST are used to decide things like the value of literals or specific operators to use.
Backpatching is used when we need to make forward jumps when we need them (look at visitIf command).
@@ -45,9 +44,9 @@ in
i := i * b
```
**b** is bound to 10 and **i** is bound to an address large enough to hold an integer.
**b** is bound to 10 and **i** is bound to an address large enough to hold an integer.
When **b** is called in the program - it should be translated to a 10 by the compiler. Each time **i** is used, it should be translated to a memory address.
When **b** is called in the program - it should be translated to a 10 by the compiler. Each time **i** is used, it should be translated to a memory address.
In this example, the address for **i** is **4**. The machine code could look like this:
@@ -69,7 +68,7 @@ In any declaration, identifiers can be bound to values or addresses, and these m
* These codes include either the literal value (in the case of constants bound to literals) or the necessary steps to work out the right address
* Each time we have a declaration, we need to generate the instructions to increase the size of the current frame; when that declaration drops out of scope, the frame is decreased in size again by “popping” elements off the stack
### Within the Triangle Compiler...
### Within the Triangle Compiler
#### Known Value
@@ -78,7 +77,7 @@ In any declaration, identifiers can be bound to values or addresses, and these m
public KnownValue(int size, int value){ ... }
...
public void encodeFetch(Emitter ...){
emitter.emit(OpCode.LOADL, 0, value)
emitter.emit(OpCode.LOADL, 0, value)
}
...
```
@@ -92,13 +91,13 @@ A **Known value** (*KnownValue.java*) is simple. *Size* is the amount of memory
public UnknownValue(int size, int level, int displacement){ ... }
...
public void encodeFetch(Emitter ...){
if (vname.indexed){
emitter.emit(OpCode.LOADA ...
emitter.emit(OpCode.CALL ...
emitter.emit(OpCode.LOADI ...
} else {
emitter.emit(OpCode.LOAD ...
}
if (vname.indexed){
emitter.emit(OpCode.LOADA ...
emitter.emit(OpCode.CALL ...
emitter.emit(OpCode.LOADI ...
} else {
emitter.emit(OpCode.LOAD ...
}
}
...
```
@@ -107,7 +106,6 @@ An **Unknown Value** is made up of two parts - level and displacement. Level is
We can use the *frame* as the displacement; that's the top of the stack.
## Code Generation: Procedures and Functions
How do we handle procedures and functions? These both translate to low level routines. A routine is a series of instructions and the template might look something like this:
@@ -125,13 +123,14 @@ h:
## Compiler Optimisations
Compiler optimisation can happen in a few places:
* Having a step before the code generation whereby the AST is manipulated or manipulating intermediate code if that is being generated.
* At the point where code is being generated - we can use processor specific instructions can be used/exploited.
* **Common Sub-expression Elimination**: attempts to reduce calculations that are repeated. (This only works when the code is simple).
* **Constant Propagation**: At some point in a program - a variable might always have the same value. An algorithm to trace the flow of constant variables through the programme can be run to determine where this is guaranteed to be the case. The reference to the variable can be replaced by a literal value - reducing the need for fetches from memory.
* **Hoisting**: If part of a computation in a loop is independent of the values that change inside the loop, it can be moved *outside* the loop - meaning that it is only calculated **once** e.g:
* **Constant Propagation**: At some point in a program - a variable might always have the same value. An algorithm to trace the flow of constant variables through the programme can be run to determine where this is guaranteed to be the case. The reference to the variable can be replaced by a literal value - reducing the need for fetches from memory.
* **Hoisting**: If part of a computation in a loop is independent of the values that change inside the loop, it can be moved *outside* the loop - meaning that it is only calculated **once** e.g:
```code
while(j < k) {
a[j] := b + c;
@@ -154,13 +153,13 @@ if (j < k){
* **Loop unrolling**: Reducing the amount of checks a program's loop (such as a *for* loop) will need. We can use some methods to half the checks or even completely remove the checks - this will result in a larger compiled program with less overhead.
* **Function Inlining**: Lifts the body of the code and places it directly to where the function was called.
**Optional reading: More examples are mentioned in the book Introduction to Compiler Design (Mogensen) cited in the module home page; also here https://compileroptimizations.com**
**Optional reading: More examples are mentioned in the book Introduction to Compiler Design (Mogensen) cited in the module home page; also here <https://compileroptimizations.com>**
## Interpreters and Native Code, JIT
Python is an interpreted language, meaning that the instructions called are done in real time - this creates more slow-down when running the program compared to a compiled language like C.
Python is an interpreted language, meaning that the instructions called are done in real time - this creates more slow-down when running the program compared to a compiled language like C.
Java is in the middle. Javac generates *bytecode* which is targeted at a *virtual machine*. This is then interpreted, so there is still some overhead involved when running with Java.
Java is in the middle. Javac generates *bytecode* which is targeted at a *virtual machine*. This is then interpreted, so there is still some overhead involved when running with Java.
Our toy language compiles into a Tam file. This is that intermediate language similar to *bytecode* in Java.
@@ -174,15 +173,15 @@ JIT (Just-in-Time) compilation was introduced to help speed up Java's slow inter
The process of targeting this extra compilation step is where the HotSpot (Oracles implementation of Java gets it name) - the targeted code is where the program runs hot.
Simply put, there's a count of the number of times each method, loop and numerous other structures are executed. If that number would reach a particular threshold, that relevant block of code is compiled natively on the machine!
Simply put, there's a count of the number of times each method, loop and numerous other structures are executed. If that number would reach a particular threshold, that relevant block of code is compiled natively on the machine!
JIT will also monitor branches of code and do the heavy work of lifting the *never executed* blocks of code from the compilation process off.
JIT will also monitor branches of code and do the heavy work of lifting the *never executed* blocks of code from the compilation process off.
* Search-based software engineering and genetic improvement of software is an active area of research that targets improvements to code that trades off possibly reduced functionality in return for greatly improved performance. Read more about it in this review paper: http://www0.cs.ucl.ac.uk/staff/J.Petke/papers/Petke_2017_TEVC.pdfLinks to an external site. or in these slides: http://geneticimprovementofsoftware.com/slides/PPSN2020_GItutorial.pdf
* Search-based software engineering and genetic improvement of software is an active area of research that targets improvements to code that trades off possibly reduced functionality in return for greatly improved performance. Read more about it in this review paper: <http://www0.cs.ucl.ac.uk/staff/J.Petke/papers/Petke_2017_TEVC.pdfLinks> to an external site. or in these slides: <http://geneticimprovementofsoftware.com/slides/PPSN2020_GItutorial.pdf>
## When and Where to Optimise Your Code
We must not prematurely optimise! We should write straight forward clean code. We don't want to write hard-to-read code; slows down development, introduce bugs that are hard to track and make maintenance so much harder.
We must not prematurely optimise! We should write straight forward clean code. We don't want to write hard-to-read code; slows down development, introduce bugs that are hard to track and make maintenance so much harder.
```java
log.log(Level.FINE, "..." + calcX() ... + calcY() ... );
@@ -192,7 +191,7 @@ This simple line has to be compiled and the method calls have to be computed i.e
```java
if(log.isLoaggable(Level.FINE)){_
log.log(Level.FINE, "..." + calcX() ... + calcY() ... );
log.log(Level.FINE, "..." + calcX() ... + calcY() ... );
}
```
-1
View File
@@ -1,2 +1 @@
# CSCU9A5 Week 8