Charm of code language

The charm of code language in this issue of technical gas station brings you three parts: a brief talk on V8 Hidden Classes and Inline Caches; Analysis of Java logic operation and bit operation; Understand Golang's type func(), and hope to help you improve your technology!

01 talking about V8 Hidden Classes and Inline Caches

JavaScript is a dynamic language based on attribute chain, and V8 is a popular JavaScript running engine. We know that the properties and types of objects can be changed at run time. To locate the attributes and types of objects, V8 introduces the concept of Hidden Classes to optimize the speed of attribute access. See the following code:

function Person(name, age) {
  this.name = name;
  this.age  = age;
}

const zhangsan = new Person('Zhangsan', 20);
const xiaofang = new Person('Xiaofang', 21);
xiaofang.gender = 'female';

When initializing the Person class, a hidden class C0 will be created for the object Zhangsan. C0 does not contain any properties. When initializing property zhangsan Name will create a new hidden Class C1 based on C0. By analogy, C2 is generated after the attribute age is initialized. When the object xiaofang is initialized, because the attribute structure is the same, it will reuse C0, C1 and C2. However, when the attribute gender is added to the object xiaofang, it will generate a new hidden class C3 based on C2.

However, if you accidentally execute the delete operation, the situation is worse, as follows:

function Person(name, age) {
    this.name = name;
    this.age  = age;
}
for (let i=0; i<1000000; i++) {
    const xiaofang = new Person('Xiaofang', 21);
    delete xiaofang.name;
}

The above code plus the delete statement takes 230ms to execute. When the delete statement is removed, it takes 4ms to execute. The reason why the gap is so large is the destruction of hidden classes. We may accidentally use delete in some scenarios, especially in large loop bodies, and the performance degradation is obvious. We can replace delete with the assignment null.

V8 further implements the Inline Caches policy to further improve the speed of attribute access. When attributes are accessed multiple times, if the parameter type, context and execution process point of a class's method can be kept unchanged, then according to the caching strategy, the search process of object attributes in the function can be avoided and the execution speed of the function can be improved. We often like to define functions in function bodies, which should be avoided as much as possible.

02 analysis of Java logic operation and bit operation

Logical operation and bit operation

Arithmetic, assignment, logic, relation, self increasing and self decreasing, condition, bit operation and other rich operations and corresponding operators are one of the main features of Java language, and also the basis for us to learn a language. In this article, we mainly briefly understand the logical operations and bit operations in Java, and how to more effectively apply their features in our actual development process.

First, understand what logical operations and bit operations are:

Logic operation: logic operation is also called boolean operation. The logical operator requires that the data type of the operand be logical, and its operation result is also a boolean value. Logical operators connect the relational expressions of various operations to form a complex logical expression to judge whether the expression in the program is true or false.

Bit operation: bit operation is an operation in binary bits. Its operands and operation results are integer values. These integer types include long, int, short, char, and byte.

operator

How do you perform these operations in Java? That is through the operator.

Logical operators: short circuit and "& &", short circuit or "|", logical non "!" Logical or "|", logical and "&".

  • &&(short circuit and) and & (logic and) difference: according to the meaning, the difference lies in "short circuit". According to the description, "if a is false, the result is false, and b will not be calculated" (because the result is false regardless of the value of b).
  • ||(short circuit or) and | (logic or) difference: the same difference as above is in "short circuit". a | b: if a is true, the result will be true, but b will continue to be calculated (all logic items will be calculated if there is no short circuit capability).

Practice I

Based on the above learning, how will you make decisions in the actual development process? Suppose you have a need to judge multiple conditions at the same time. Will you consciously sort these conditions or simply list them out of order? Would you choose option 1 or option 2?

public class Test {
    public static void main(String[] args) {
        // Scenario 1, a () & & B ()
        System.out.println(a() && b());
        // Scenario 2, B () & & A ()
        System.out.println(b() && a());
    }

    /**
     * A time-consuming logical judgment
     */
    public static boolean a() {
        System.out.println("do a!");
        boolean a = false;
        // Time consuming and performance consuming logic
        for (int i = 0; i < 1000000; i++) {
            // time-consuming operation 
            a = true;
        }
        return a;
    }

    /**
     * A simple logical judgment
     */
    public static boolean b() {
        System.out.println("do b!");
        return false;
    }
}

Everyone must be able to make the right choice. This is actually a very small knowledge point, but many people will ignore it in the normal development process. We should pay attention to details in the daily development process. The more basic logic is, the more energy can be accumulated, and good programming habits and performance awareness can be developed.

Short circuit and (& &) and short circuit or (||) due to the existence of short circuit mechanism, it can optimize the calculation of logic operation and improve the efficiency. In the actual development process, we should consciously use the short-circuit ability of short-circuit and short-circuit or to optimize our logic operations.

Bitwise operators: include bitwise logical operators (bitwise and'& ', bitwise or'|', bitwise non'~', bitwise exclusive or'^'), displacement operators (shift right'> > ', shift left'<<', shift right to fill zero'> > > ')

The logic is relatively simple. We won't go into details. We will mainly look at how bit operations are applied to our daily practical development. Maybe we will find that we do not often use in place computing in our daily development, but we can often see the corresponding use when looking at the Android source code. For example, mPrivateFlags, mPrivateFlags1, mPrivateFlags2, mPrivateFlags3, etc. in View, one variable can even store dozens of different states. This is an advantage that can be achieved through bit operation. It can make "multi state" management as simple and efficient as single state management.

Practice II

Suppose there are seven colors, and an object can have multiple colors at the same time, how can we simply and efficiently maintain the color attribute of the object?

1. Maintain a color attribute set, add a color to the set for each color you own, and delete it if you remove it?

2. Maintain 7 Boolean values to point to different color values. Each Boolean value is set to true. Otherwise, it is set to false?

The above is just a few simple examples. For example, we use Boolean values to maintain states, which is also the most commonly used method. This kind of maintenance for single States is very simple and intuitive, but it is not so suitable for multi state maintenance.

At this point, we can try to consider using bit operation. Review the bitwise and bitwise or of bitwise operation. If the bitwise or is different from the corresponding bit, it will be 1. Is this equivalent to adding a state? Similarly, when the bitwise and corresponding bits are both 1, it is 1. Can you judge whether there is a certain status? We can use the 1 of different bits to represent different states. The 1 of the final result indicates which state it has. In this way, we can manage multiple states with one attribute at the same time.

public class Test {
    /** Single attribute maintenance multi status */
    private static int mColors = 0B00000000;
    private static final int RED = 0B00000001;
    private static final int ORANGE = 0B00000010;
    private static final int YELLOW = 0B00000100;
    private static final int GREEN = 0B00001000;
    private static final int BLUE = 0B00010000;
    private static final int PURPLE = 0B00100000;
    private static final int PINK = 0B01000000;

    public static void main(String[] args) {
        // Red, green and blue, mColors = 0B00011001
        mColors = mColors | RED | GREEN | BLUE;
        System.out.println(mColors);
        // Judge whether it has red color, true
        System.out.println((mColors & RED) != 0);
        // Judge whether there is yellow, false
        System.out.println((mColors & YELLOW) != 0);
        // Add yellow, mColors = 0B00011101
        mColors |= YELLOW;
        // Judge whether there is yellow, true
        System.out.println((mColors & YELLOW) != 0);
        // Remove yellow, mColors = 0B00011001
        mColors &= ~YELLOW;
        // Judge whether there is yellow, false
        System.out.println((mColors & YELLOW) != 0);
    }
}

summary

There are some characteristics in basic grammar that are easy to be ignored, but sometimes they are of great use. Mastering these characteristics can provide us with more possibilities to solve practical problems and broaden our horizons.

03 understand Golang's type func()

In the Go language, you can use the type keyword to customize the type, such as the common:

type Myint int // MyInt type is defined. Its basic type is int. it has the same underlying data structure as int, but it is completely different from int
type Book struct {  // A Book structure is defined, including two fields: Title and PageNum
    Title    string
    PageNum  int
}

Similarly, the Go language also supports user-defined function types. Functions with the same parameter and return value list (excluding parameter name and function name, and requiring the type and order of parameter and return value list to be consistent) are regarded as the same type. Since functions can be defined as a type, similarly, in Go language, functions can be treated as ordinary values like other types, and then transferred between other functions, assigned variables, judged types and converted types. For example:

type PowerCanculator func(num int) int   // Power calculation
 
func PowerBase2(num int) int {    // Power calculation based on 2
    return 1 << num
}
 
func main() {
    var pc PowerCanculator
    var result int
    pc = PowerBase2
    result = pc(2)
    fmt.Println(result) // The result is 4
}

In the above example, PowerBase2, as an implementation of powercalculator type, is assigned as a parameter to a function variable of powercalculator type and called.

So what are the benefits or advantages of passing functions as parameters? We can consider a scenario: we want to have a calculator that can perform arbitrary numerical calculations, and all parameters and specific operations are given by the caller. In this way, our calculator can only perform unified scheduling. How can we implement this?

type PowerCanculator func(num int) int   // Power calculation
  
func PowerBase2(num int) int {    // Power calculation based on 2
    return 1 << num
}
 
func PowerBase4(num int) int {    // Power calculation based on 4
    return 1 << (num+1)
}
 
func Canculator(num int, pc PowerCanculator) (int, error) {
    if pc == nil {
        return 0,errors.New("param error")
    }
    return pc(num),nil
}
 
func main() {
   result1, err := Canculator(2, PowerBase2)
   if err != nil {
      fmt.Println(err)
   }
   fmt.Println(result1) //The result is 4
   result2, err := Canculator(2, PowerBase4)
   if err != nil {
      fmt.Println(err)
   }
   fmt.Println(result2) //The result is 16
}

The above is an example of a function being passed in as a parameter of another function. Since a function is treated as a normal parameter, can it also be returned as the return value of another function? The answer is yes:

fun PowerCanculatorGenerator(base int)  PowerCanculator {
    switch base{
    case 2:
        return PowerBase2
    case 4:
        return PowerBase4
    default:
        return nil
    }
}

func main() {
    var base int
    var powerBase2 PowerCanculator
    var powerBase4 PowerCanculator
    
    powerBase2 = PowerCanculatorGenerator(2)    
    powerBase4 = PowerCanculatorGenerator(4)
    fmt.Println(powerBase2(2)) //The result is 4
    fmt.Println(powerBase4(2)) //The result is 16
}

The above is a simple example. In fact, the most typical application scenario of function types is when http service route registration is performed. For example, there are two http request URIs, namely /sayhi and /saybye. When http request implementation is performed, it can be written as follows:

func mHttp() {
    http.HandleFunc("/sayhi", hi)   
    http.HandleFunc("/sayBye", bye)
    http.ListenAndServe("0.0.0.0:8888",nil)
}
 
func hi(w http.ResponseWriter, r *http.Request) {
 
}
 
func bye(w http.ResponseWriter, r *http.Request) {
 
}

There is a function type in the http package:

type HandlerFunc func(ResponseWriter, *Request)

It is not difficult to see that HandlerFunc is a function type with two parameters, while hi and bye are the implementation of HandlerFunc. By specifying the value of the function variable in HandlerFunc, the routing registration of different services can be realized.

In fact, the above contents are generally explaining two key features of functions:

1. it can be passed into a function as a parameter;

2. it can be returned from the function as a return value.

In fact, functions are called "first-class citizens" in the Go language. In addition to the basic features of functions and the above two features, they also have more special applications, such as explicit type conversion of functions. I won't Go into details here. Interested students can consult relevant articles and books by themselves~

----------  END  ----------

It is recommended to read [technical gas station] series:

Baidu programmer Android development tips

Chrome Devtools debugging tips

Discussion on the super large scale pre training model of artificial intelligence

Tags: CODE

Posted by plimpton on Wed, 01 Jun 2022 14:03:50 +0530