esbuild从去年过年后刚知道的时候,就用它来跑react项目虽然结果失败了,但使用go作为编译工具却在我脑海中埋下了种子。
go真的比nodejs快吗?事实胜于雄辩,脚本语言慢真的是天生的。下面是nodejs和go做做100000以内的求和实验
//nodejs代码
console.time("test");
var sum = 0;
var target = 100000;
for (let i = 0; i < target; i++) {
sum += i;
}
console.log("JS:sum:", sum);
console.timeEnd("test");
//golang代码
package main
import (
"fmt"
"time"
)
func main() {
start := time.Now()
var sum = 0
var target = 100000
for i := 0; i < target; i++ {
sum += i
}
fmt.Println("GO:sum:", sum)
cost := time.Since(start)
fmt.Println("Runtime:", cost)
}
语言 | 执行时间 |
---|---|
nodejs | 13.074ms |
nodejs | 15.89ms |
nodejs | 14.844ms |
nodejs | 13.337ms |
nodejs | 13.316ms |
平均耗时 | 14.1448ms |
语言 | 执行时间 |
---|---|
golang | 67.087µs |
golang | 66.343µs |
golang | 73.034µs |
golang | 71.219µs |
golang | 68.233µs |
平均耗时 | 69.1832µs |
编译器主要任务之一就是报告它在翻译过程中的错误
如果目标程序是可执行的机器语言程序,他可以被调用,处理输入并产生输出
func main() {
// 统计计时
start := time.Now()
// 读取文件
file, err := os.Open("test.less")
if err != nil {
fmt.Printf("Error: %s\n", err)
return
}
defer file.Close()
// 缓存
br := bufio.NewReader(fi)
for {
// 按行读取
line, _, c := br.ReadLine()
if c == io.EOF {
break
}
// 读取生成token
pkg.ReadLine1(line)
}
//声明ast根对象
var astData pkg.DataNode
astData.SelectName = "root"
astData.Children = make([]pkg.DataNode, 0)
//根据token遍历生成整个ast对象树
astData = pkg.GenerateAST(astData)
//深度优先遍历生成结果字符串
astDataString := pkg.GenerateChild(astData)
//判断目标文件是否存在如果存在就删除,保证新文件生成
if pkg.CheckFileIsExist("test.css") {
_ = os.Remove("test.css")
}
//写入目标文件
pkg.WriteFile1(astDataString)
//计算运行耗时
cost := time.Since(start)
fmt.Println("Runtime:", cost)
}
// token结构体
// 示例
// {
// TypeName: "Select", // 选择器
// Value: "#video" // 选择器值
// }
type Token struct {
TypeName string
Value string
}
// Attr 属性结构体
// 示例
// {
// Name: "width", // 属性名称
// Value: "100px" // 属性值
// }
type Attr struct {
Name string
Value string
}
// ast 结构体
type DataNode struct {
SelectName string
Declarations []Attr
Children []DataNode
}
// 读取行字节生成token
func ReadLine1(lineData []byte) {
dataString := string(lineData)
// less 变量罗例如 @big:100px
if strings.HasPrefix(dataString, "@") {
// 变量缓存操作
variableFormat(dataString)
return
}
// 样式开头行例如 #video {
if strings.Index(dataString, "{") >= 0 {
index := strings.Index(dataString, "{")
selectToken := Token{
TypeName: "Select",
Value: TrimSpace(dataString[:index]),
}
Tokens = append(Tokens, selectToken)
}
// 样式结尾行例如 }
if strings.Index(dataString, "}") >= 0 {
PunctuatorToken := Token{
TypeName: "Punctuator",
Value: "}",
}
Tokens = append(Tokens, PunctuatorToken)
}
// 属性中间行例如 width:100px;
if strings.Index(dataString, ":") >= 0 {
// 样式属性名称 例如 width
index := strings.Index(dataString, ":")
before := TrimSpace(dataString[:index])
attributeToken := Token{
TypeName: "Attribute",
Value: before,
}
Tokens = append(Tokens, attributeToken)
indexEnd := len(dataString) - 1
// 属性值是不是在less定义的变量中
value := TrimSpace(dataString[index+1 : indexEnd])
_, ok := variableMap[value]
// 样式属性值例如@big或者100px
if ok {
attrValue := variableMap[value]
ValueToken := Token{
TypeName: "Value",
Value: attrValue,
}
Tokens = append(Tokens, ValueToken)
} else {
ValueToken := Token{
TypeName: "Value",
Value: value,
}
Tokens = append(Tokens, ValueToken)
}
}
}
// tokens 列表
// index token的索引
// characterList符号表
func GenerateChildren1(tokens []Token, index int, characterList []string) (children DataNode, i int) {
child := DataNode{}
attr := Attr{}
// less会出现层级嵌套的情况
// #body{
// #child{
// }
// }
var isBodyClose = false
conNum := -1
for childIndex := 0; childIndex < len(tokens); childIndex++ {
if childIndex <= conNum {
continue
}
token := tokens[childIndex]
//是否是第一层body嵌套
if token.TypeName == "Select" && !isBodyClose {
isBodyClose = true
child = generateChildNode1(token, characterList)
characterList = append(characterList, token.Value)
continue
// 如果是第二层就是child节点进行递归算法
} else if token.TypeName == "Select" && isBodyClose {
childTokens := tokens[childIndex:]
childNode, i := GenerateChildren1(childTokens, childIndex, characterList)
conNum = i
child.Children = append(child.Children, childNode)
continue
// body体结束
} else if token.Value == "}" {
return child, index + childIndex
// 存入当前选择器的样式属性包括属性名称和属性值
} else if token.TypeName == "Attribute" {
attr.Name = token.Value
attr.Value = tokens[childIndex+1].Value
child.Declarations = append(child.Declarations, attr)
conNum = childIndex + 1
}
}
return children, index
}
//child 抽象语法树节点生成字符串
func GenerateChild(child DataNode) string {
stringLines := child.SelectName + " {" + "\n"
declarations := child.Declarations
for _, declaration := range declarations {
stringLines += " " + declaration.Name + ": " + declaration.Value + ";\n"
}
// 判断body中是否为空属性
if strings.HasSuffix(stringLines, "{\n") {
stringLines = ""
} else {
stringLines += "}\n"
}
// 递归遍历子节点
for _, childNode := range child.Children {
stringLines += GenerateChild(childNode)
}
return stringLines
}
// 写入文件
func WriteFile1(data string) {
f, _ := os.Create("test.css")
f.Close()
file, err := os.OpenFile("test.css", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file error=%v\n", err)
return
}
defer file.Close()
// _, err = f.Write([]byte("要写入的文本内容"))
write := bufio.NewWriter(file)
write.WriteString(data)
write.Flush()
}
func main() {
start := time.Now()
var sum = 0
var target = 100000
// 并发加锁
var waitGroup sync.WaitGroup
var mutex sync.Mutex
for i := 0; i < target; i++ {
waitGroup.Add(1)
go func(val int) {
mutex.Lock()
sum += val
mutex.Unlock()
waitGroup.Done()
}(i)
}
waitGroup.Wait()
fmt.Println("GO:sum:", sum)
cost := time.Since(start)
fmt.Println("Runtime:", cost)
}