Golang 编程“珠玑”

《编程珠玑》源代码 《编程珠玑》第一版是我早期职业生涯中阅读过的对我影响较大的书籍之一,在书中首次接触到的很多观点都让我长期受益。作者在这一版本中做了重要更新。新增加的很多例子让我耳目一新。 ——Steve McConnell,《代码大全》作者 如果让程序员们列举他们喜欢的书籍,Jon Bentley的《编程珠玑》一定可以归于经典之列。如同精美的珍珠出自饱受沙砾折磨的牡蛎,程序员们的精彩设计也来源于曾经折磨他们的实际问题。Bentley的“珍珠”基于坚实的工程学基础,为那些麻烦的问题提供了富于智慧性和创造性的解决方案。本书通过一些有趣的具有指导意义的程序,对程序设计技巧和基本设计原理进行了清晰而睿智的描述。因此本书对各个层次的程序员都具有很高的价值。 立即下载

作者:崔国科—— MO 研发工程师

导读

2017 年左右开始接触 golang,那时国内关于 golang 的资料还很少。

现在随着云原生生态的蓬勃发展,在 kubernetes、docker 等等众多明星项目的带动下,国内有越来越多的创业公司以及大厂开始拥抱 golang,各种介绍 golang 的书籍、博客和公众号文章也变得越来越多,其中不乏质量极高的资料。

相关的资料已经足够丰富,因此这篇文章不会详述 golang 的某一个方面,而是主要从工程实践的角度出发,去介绍一些东西。因为在工作过程中,我注意到一些令人沮丧的代码,其中有些甚至来自于高级程序员。

下面是本文目录概览,我们将从内存有关的话题开始:

  1. 内存相关
  2. Golang Profiling
  3. 如何写性能测试

Part 1 内存相关

1.1 编译器内存逃逸分析

先看这样一段代码:

package main

//go:noinline
func makeBuffer() []byte {
    return make([]byte, 1024)
}

func main() {
    buf := makeBuffer()
    for i := range buf {
        buf[i] = buf[i] + 1
    }
}

示例代码中函数 makeBuffer 返回的内存位于函数栈上,在 C 语言中,这是一段错误的代码,会导致未定义的行为。

在 Go 语言中,这样的写法是允许的,Go 编译器会执行 escape analysis:当它发现一段内存不能放置在函数栈上时,会将这段内存放置在堆内存上。例如,makeBuffer 向上返回栈内存,编译器自动将这段内存放在堆内存上。

通过 -m 选项可以查看编译器分析结果:

$ go build -gcflags="-m" escape.go
# command-line-arguments
./escape.go:8:6: can inline main
./escape.go:5:13: make([]byte, 1024) escapes to heap

除此之外,也存在其他一些情况会触发内存的“逃逸”:

  • 全局变量,因为它们可能被多个 goroutine 并发访问;
  • 通过 channel 传送指针
type Hello struct { name string }
ch := make(chan *Hello, 1)
ch <- &Hello{ name: "world"}
  • 通过 channel 传送的结构体中持有指针
type Hello struct { name *string }
ch := make(chan *Hello, 1)
name := "world"
ch <- Hello{ name: &name }
  • 局部变量过大,无法放在函数栈上
  • 本地变量的大小在编译时未知,例如 s := make([]int, 1024) 也许不会被放在堆内存上,但是 s := make([]int, n) 会被放在堆内存上,因为其大小 n 是个变量
  • 对 slice 的 append 操作触发了其底层数组重新分配

注意:上面列出的情况不是详尽的,并且可能随 Go 的演进发生变化。

在开发过程中,如果程序员不注意 golang 编译器的内存逃逸分析,写出的代码可能会导致“额外”的动态内存分配,而 “额外”的动态内存分配通常会和性能问题联系在一起(具体会在后面 golang gc 的章节中介绍)。

示例代码给我们的启示是:注意函数签名设计,尽量避免因函数签名设计不合理而导致的不必要内存分配。向上返回一个 slice 可能会触发内存逃逸,向下传入一个 slice 则不会,这方面 Cockroach Encoding Function 给出了一个很好的例子。

接下来,我们看下接口相关的事情。

1.2 interface{} / any

any 是 golang 1.18 版本引入的,跟 interface{} 等价。

type any = interface{}

在 golang 中,接口实现 为一个“胖”指针:一个指向实际的数据,一个指向函数指针表(类似于C++ 中的虚函数表)。

我们来看下面的代码:

package interfaces

import (
  "testing"
)

var global interface{}

func BenchmarkInterface(b *testing.B) {
  var local interface{}
  for i := 0; i < b.N; i++ {
    local = calculate(i) // assign value to interface{}
  }
  global = local
}

// values is bigger than single machine word.
type values struct {
  value  int
  double int
  triple int
}

func calculate(i int) values {
  return values{
    value:  i,
    double: i * 2,
    triple: i * 3,
  }
}

在性能测试 BenchmarkInterface 中,我们将函数 calculate 返回的结果赋值给 interface{} 类型的变量。

接下来,对 BenchmarkInterface 执行 memory profile:

$ go test -run none -bench Interface -benchmem -memprofile mem.out

goos: darwin
goarch: arm64
pkg: github.com/cnutshell/go-pearls/memory/interfaces
BenchmarkInterface-8    101292834               11.80 ns/op           24 B/op          1 allocs/op
PASS
ok      github.com/cnutshell/go-pearls/memory/interfaces        2.759s

$ go tool pprof -alloc_space -flat mem.out
(pprof) top 
(pprof) list iface.BenchmarkInterface
Total: 2.31GB
    2.31GB     2.31GB (flat, cum) 99.89% of Total
         .          .      7:var global interface{}
         .          .      8:
         .          .      9:func BenchmarkInterface(b *testing.B) {
         .          .     10:   var local interface{}
         .          .     11:   for i := 0; i < b.N; i++ {
    2.31GB     2.31GB     12:           local = calculate(i) // assign value to interface{}
         .          .     13:   }
         .          .     14:   global = local
         .          .     15:}
         .          .     16:
         .          .     17:// values is bigger than single machine word.
(pprof)

从内存剖析结果看到:向接口类型的变量 local 赋值,会触发内存“逃逸”,导致额外的动态内存分配。

go 1.18 引入范型之前,我们都是基于接口实现多态,基于接口实现多态,主要存在下面这些问题:

  1. 丢失了类型信息 ,程序行为从编译阶段转移到运行阶段;
  2. 程序运行阶段不可避免地需要执行类型转换,类型断言或者反射等操作;
  3. 为接口类型的变量赋值可能会导致“额外的”内存分配;
  4. 基于接口的函数调用,实际的调用开销为:指针解引用(确定方法地址)+ 函数执行开销。编译器无法对其执行内联优化,也无法基于内联优化执行进一步的优化。

关于接口的使用,这里有一些提示:

  • 代码中避免使用 interface{} 或者 any,至少避免在被频繁使用的数据结构或者函数中使用
  • go 1.18 引入了范型,将接口类型改为范型类型,是避免额外内存分配,优化程序性能的一个手段

1.3 Golang gc

前面我们了解到,golang 编译器执行 escape analysis 后,根据需要数据可能被“搬”到堆内存上。

这里简单地介绍下 golang 的 gc,从而了解写 golang 代码时为什么应该尽量避免“额外的”内存分配。

1.3.1 Introduction

gc 是 go 语言非常重要的一部分,它大大简化了程序员写并发程序的复杂度。

人们发现写工作良好的并发程序似乎也不再是那少部分程序员的独有技能。

glang gc 使用一棵树来维护堆内存对象的引用,属于追踪式的 gc,它基于“标记-清除“算法工作,主要分为两个阶段:

  1. 标记阶段- 遍历所有堆内存对象,判断这些对象是否在用;
  2. 清除阶段- 遍历树,清除没有被引用的堆内存对象。

执行 gc 时,golang 首先会执行一系列操作并停止应用程序的执行,即 stopping the world,之后恢复应用程序的执行,同时 gc 其他相关的操作还会并行地执行。所以 golang 的 gc 也被称为 concurrent mark-and-sweep,这样做的目的是尽可能减少 STW 对程序运行的影响。

严格地说,STW 会发生两次,分别在标记开始和标记结束时。

golang gc 包括一个 scavenger,定期将不再使用的内存返还给操作系统。

也可以在程序中调用 debug.FreeOSMemory(),手动将内存返还给操作系统。

1.3.2 gc 触发机制

相比于 java,golang 提供的 gc 控制方式比较简单:通过环境变量 GOGC 来控制。

runtime/debug.SetGCPercent allows changing this percentage at run time.

GOGC 定义了触发下次 gc 时堆内存的增长率,默认值为 100,即上次 gc 后,堆内存增长一倍时,触发另一次 gc。

例如,gc 触发时当前堆内存的大小时 128MB,如果 GOGC=100,那么当堆内存增长为 256MB时,执行下一次 gc。

另外,如果 golang 两分钟内没有执行过 gc,会强制触发一次。

我们也可以在程序中调用 runtime.GC() 主动触发 gc。

# 通过设置环境变量 GODEBUG 可以显示 gc trace 信息

$ GODEBUG=gctrace=1 go test -bench=. -v

# 当 gc 运行时,相关信息会写到标准错误中

注意:为了减少 gc 触发次数而增加 GOGC 值并不一定能带来线性的收益,因为即便 gc 触发次数变少了,但是 gc 的执行可能会因为更大的堆内存而有所延长。在大多数情况下,GOGC 维持在默认值 100 即可。

1.3.3 gc hints 

如果我们的代码中存在大量“额外”的堆内存分配,尤其是在代码关键路径上,对于性能的负面影响是非常大的:

  • 首先,堆内存的分配本身就是相对耗时的操作
  • 其次,大量“额外”的堆内存分配意味着额外的 gc 过程,STW 会进一步影响程序执行效率。

极端情况下,短时间内大量的堆内存分配,可能会直接触发 OOM,gc 甚至都没有执行的机会。

所以,不要“天真”的以为 gc 会帮你搞定所有的事情:你留给 gc 处理的工作越少,你的性能才会越“体面”。

从性能优化的角度,消除那些“额外的”内存分配收益十分明显,通常也会是第一或者第二优先的选项。

然而,堆内存的使用并不能完全避免,当需要使用时,可以考虑采用某些技术,例如通过 sync.Pool 复用内存来减少 gc 压力。

1.3.4 有了 gc 为什么还会有内存泄漏?

即便 golang 是 gc 语言,它并不是一定没有内存泄漏,下面两种情况会导致内存泄漏的情况:

  1. 引用堆内存对象的对象长期存在;
  2. goroutine 需要消耗一定的内存来保存用户代码的上下文信息,goroutine 泄漏会导致内存泄漏。

1.3.5 代码演示

代码见于文件 gc.go

  • 函数 allocator 通过 channel 传送 buf 类型的结构体,buf 类型的结构体持有堆内存的引用;
  • 函数 mempool 通过 channel 接收来自 allocator 的 buf,循环记录在 slice 中;
  • 同时,mempool 还会定期打印应用当前内存状态,具体含义参考 runtime.MemStats。

运行代码 gc.go:

$ go run gc.go
HeapSys(bytes),PoolSize(MiB),HeapAlloc(MiB),HeapInuse(MiB),HeapIdle(bytes),HeapReleased(bytes)
 12222464,     5.00,     7.11,     7.45,  4415488,  4300800
 16384000,    10.00,    12.11,    12.45,  3334144,  3153920
 24772608,    18.00,    20.11,    20.45,  3334144,  3121152
 28966912,    22.00,    24.11,    24.45,  3334144,  3121152
 33161216,    25.00,    27.11,    27.45,  4382720,  4169728
 37355520,    32.00,    34.11,    34.45,  1236992,   991232
 41549824,    36.00,    38.11,    38.45,  1236992,   991232
 54132736,    48.00,    50.11,    50.45,  1236992,   991232
 58327040,    51.00,    53.11,    53.45,  2285568,  2039808

通过程序输出结果,我们可以了解到:如果程序中存在变量持有对堆内存的引用,那么这块堆内存不会被 gc 回收。

因此使用引用了堆内存的变量赋值时,例如将其赋值给新的变量,需要注意避免出现内存泄漏。通常建议将赋值有关的操作封装在方法中,以通过合理的 API 设计避免出现“意想不到”内存泄露。并且封装还带来的好处是提高了代码的可测性。

1.3.6 参考资料


Part 2 Golang Profiling

profiler 运行用户程序,同时配置操作系统定期送出 SIGPROF 信号:

  • 收到 SIGPRFO 信号后,暂停用户程序执行;
  • profiler 搜集用户程序运行状态;
  • 搜集完毕恢复用户程序执行。

如此循环。

profiler 是基于采样的,对程序性能存在一定程度的影响。

"Before you profile, you must have a stable environment to get repeatable results."

2.1 Supported Profiling

By default, all the profiles are listed in runtime/pprof.Profile

a. CPU Profiling

CPU profiling 使能后,golang runtime 默认每 10ms 中断应用程序,并记录 goroutine 的堆栈信息。

b. Memory Profiling

Memory profiling 和 CPU profiling 一样,也是基于采样的,它会在堆内存分配时记录堆栈信息。

默认每 1000 次堆内存分配会采样一次,这个频率可以配置。

注意:Memory profiling 仅记录堆内存分配信息,忽略栈内存的使用。

c. Block Profiling

Block profiling 类似于 CPU profiling,不过它记录 goroutine 在共享资源上等待的时间。

它对于检查应用的并发瓶颈很有帮助,Blocking 统计对象主要包括:

  • 读/写 unbuffered channel
  • 写 full buffer channel,读 empty buffer channel
  • 加锁操作

如果基于 net/http/pprof, 应用程序中需要调用 runtime.SetBlockProfileRate 配置采样频率。

d. Mutex Profiling

Go 1.8 引入了 mutex profile,允许你捕获一部分竞争锁的 goroutines 的堆栈。

如果基于 net/http/pprof, 应用程序中需要调用 runtime.SetMutexProfileFraction 配置采样频率。

注意:通过 net/http/pprof 对线上服务执行 profiling 时,不建议修改 golang profiler 默认值,因为某些 profiler 参数的修改,例如增加 memory profile sample rate,可能会导致程序性能出现明显的降级,除非你明确的知道可能造成的影响。

2.2 Profiling Commands

我们可以从 go test 命令,或者从使用 net/http/pprof 的应用中获取到 profile 文件:

## 1. From unit tests
$ go test [-blockprofile | -cpuprofile | -memprofile | -mutexprofile] xxx.out

## 2. From long-running program with `net/http/pprof` enabled
## 2.1 heap profile
$ curl -o mem.out http://localhost:6060/debug/pprof/heap

## 2.2 cpu profile
$ curl -o cpu.out http://localhost:6060/debug/pprof/profile?seconds=30

获取到 profile 文件之后,通过 go tool pprof 进行分析:

# 1. View local profile
$ go tool pprof xxx.out

# 2. View profile via http endpoint
$ go tool pprof http://localhost:6060/debug/pprof/block
$ go tool pprof http://localhost:6060/debug/pprof/mutex

2.3 Golang Trace

我们可以从 go test 命令,或者从使用 net/http/pprof 的应用中获取到 trace 文件:

# 1. From unit test
$ go test -trace trace.out

# 2. From long-running program with `net/http/pprof` enabled
curl -o trace.out http://localhost:6060/debug/pprof/trace?seconds=5

获取到 trace 文件之后,通过 go tool trace 进行分析,会自动打开浏览器:

$ go tool trace trace.out

2.4 Profiling Hints

如果大量时间消耗在函数 runtime.mallocgc,意味着程序发生了大量堆内存分配,通过 Memory Profiling 可以确定分配堆内存的代码在哪里;

如果大量的时间消耗在同步原语(例如 channel,锁等等)上,程序可能存在并发问题,通常意味着程序工作流程需要重新设计;

如果大量的时间消耗在 syscall.Read/Write,那么程序有可能执行大量小 IO;

如果 GC 组件消耗了大量的时间,程序可能分配了大量的小内存,或者分配的堆内存比较大。

2.5 代码演示

代码见于文件 contention_test.go:

  • Block Profiling with Unit Test
$ go test -run ^TestContention$ -blockprofile block.out
$ go tool pprof block.out
(pprof) top
(pprof) web
  • Mutex Profiling with Unit Test
$ go test -run ^TestContention$ -mutexprofile mutex.out
$ go tool pprof mutex.out
(pprof) top
(pprof) web
  • Trace with Unit Test
$ go test -run ^TestContention$ -trace trace.out
$ go tool trace trace.out

2.6 参考资料


Part 3 如何写性能测试

性能问题不是猜测出来的,即便我们“强烈的认为”某处代码是性能瓶颈,也必须经过验证。

"Those who can make you believe absurdities can make you commit atrocities" - Voltaire

对于性能测试来说,很容易写出不准确的 Benchmark,从而形成错误的印象。

3.1 Reset or Pause timer

func BenchmarkFoo(b *testing.B) {
  heavySetup()  // 在 for 循环之前执行设置工作,如果设置工作比较耗时,那么会影响测试结果的准确性
  for i := 0; i < b.N; i++ {
    foo()
  }
}

优化方式

func BenchmarkFoo(b *testing.B) {
  heavySetup()
  b.ResetTimer()  // 重置 timer,保证测试结果的准确性
  for i := 0; i < b.N; i++ {
    foo()
  }
}

如何停止timer

func BenchmarkFoo(b *testing.B) {
  for i := 0; i < b.N; i++ {
    b.StopTimer() // 停止 timer
    heavySetup()
    b.StartTimer() // 启动 timer
    foo()
  }
}

3.2 提高测试结果可信度

对于 Benchmark,有很多因素会影响结果的准确性:

  • 机器负载情况
  • 电源管理设置
  • 热扩展(thermal scaling)
  • ……

相同的性能测试代码,在不同的架构,操作系统下运行可能会产生截然不同的结果;

相同的 Benchmark 即便在同一台机器运行,前后也可能产生不一致的数据。

简单的方式是增加 Benchmark 运行次数或者多次运行测试来获取相对准确的结果:

  • 通过 -benchtime 设置性能测试时间(默认 1秒)
  • 通过 -count 多次运行 Benchmark
package benchmark

import (
        "sync/atomic"
        "testing"
)

func BenchmarkAtomicStoreInt32(b *testing.B) {
        var v int32
        for i := 0; i < b.N; i++ {
                atomic.StoreInt32(&v, 1)
        }
}

func BenchmarkAtomicStoreInt64(b *testing.B) {
        var v int64
        for i := 0; i < b.N; i++ {
                atomic.StoreInt64(&v, 1)
        }
}

多次运行测试,得出置信度较高的结果:

$ go test -bench Atomic -count 10 | tee stats.txt

$ benchstat stats.txt
goos: darwin
goarch: arm64
pkg: github.com/cnutshell/go-pearls/benchmark
                   │   stats.txt   │
                   │    sec/op     │
AtomicStoreInt32-8   0.3131n ± ∞ ¹
AtomicStoreInt64-8   0.3129n ± ∞ ¹
geomean              0.3130n
¹ need >= 6 samples for confidence interval at level 0.95
如果提示 benchstat 未找到,通过 go install 命令安装:go install  http://golang.org/x/perf/cmd/benchstat@latest

3.3 注意编译器优化

package benchmark

import "testing"

const (
        m1 = 0x5555555555555555
        m2 = 0x3333333333333333
        m4 = 0x0f0f0f0f0f0f0f0f
)

func calculate(x uint64) uint64 {
        x -= (x >> 1) & m1
        x = (x & m2) + ((x >> 2) & m2)
        return (x + (x >> 4)) & m4
}

func BenchmarkCalculate(b *testing.B) {
        for i := 0; i < b.N; i++ {
                calculate(uint64(i))
        }
}

func BenchmarkCalculateEmpty(b *testing.B) {
        for i := 0; i < b.N; i++ {
                // empty body
        }
}

运行示例代码中的测试,两个测试的结果相同:

$ go test -bench Calculate
goos: darwin
goarch: arm64
pkg: github.com/cnutshell/go-pearls/benchmark
BenchmarkCalculate-8            1000000000               0.3196 ns/op
BenchmarkCalculateEmpty-8       1000000000               0.3154 ns/op
PASS
ok      github.com/cnutshell/go-pearls/benchmark        0.814s

那么如何避免这种情况呢,前面介绍 golang 接口的时候,给出了一个例子:

var global interface{}

func BenchmarkInterface(b *testing.B) {
  var local interface{}
  for i := 0; i < b.N; i++ {
    local = calculate(uint64(i)) // assign value to interface{}
  }
  global = local
}

将被 calculate 的返回值赋给本地变量 local,循环结束后将本地变量 local 赋值给一个全局变量 global,这样可以避免函数 calculate 被编译器优化掉。

3.4 总结

错误的性能测试结果会导致我们做出错误的决定,正所谓“差之毫厘,谬以千里”,写性能测试代码并不是表面上看起来的那么简单。

Vivado时序违约小结 目录1. setup time违约1.1 逻辑级数太高1.2 Fanout太大2. hold time 违约2.1 异步信号处理导致hold time违约 1. setup time违约 建立时间是工程设计中最常遇到的问题了。一般说来,导致建立时间违例主要有两个原因:逻辑级数太大或者扇出太大,当然也可以尝试通过降频来解决建立时间违约。 1.1 逻辑级数太高 Levels指的是逻辑级数logic level,一个logic level的延迟对应的是一个LUT和一个Net的延迟,对于不同的器件,不同频率的设计能 阅读详情

相关推荐

医疗AI Agent实战:从零搭建一个能看病的智能助手(附完整代码)

本文详细介绍了如何从零搭建一个医疗AI Agent智能助手,涵盖核心架构设计、症状推理引擎开发、安全与合规实现以及系统集成等关键环节。通过完整的代码示例和实战案例,帮助开发者快速构建具备医疗咨询能力的智能Agent,提升医疗服务的效率和质量。

ss78901的博客 469

golang profiling

这个是低级别的, 需要你手动做一些设置等等周边工作, 不利于我们快速上手, 利用 pprof 帮助我们解决实际的问题。针对于应用程序, 通过命令行的启动参数来生成 prof 文件, 再使用 go tool pprof 工具进行分析。进入 30 秒的 profile 收集时间, 在这段时间内请求服务, 尽量让 cpu 占用性能产生数据。针对测试服务端, 进行操作, 上述步骤默认监听 30s, 即 30s 后可以生成相关图像。适用于运行一次的数据采集。生成的文件需要命令行进行调试, 非常不方便, 不推荐。...

云满笔记 1026

避坑指南:C#调用CodeSoft打印标签时常见的5个错误及解决方法

本文总结了C#调用CodeSoft打印标签时常见的5个核心错误及解决方案,涵盖环境配置、模板加载、变量赋值、打印机切换和资源管理。重点解析了如何避免COM对象创建失败、模板路径错误、变量名不匹配、打印机切换异常及内存泄漏等问题,帮助开发者实现稳定高效的标签打印集成。

weixin_29215815的博客 232

golang 1. 启动一个协程,将1-2000的数放入到一个channel中,比如numChan 2. 启动八个协程,从numChan 取出数(比如n),并计算1+...+n的值

// 要求 // 1. 启动一个协程,将1-2000的数放入到一个channel中,比如numChan // 2. 启动八个协程,从numChan 取出数(比如n),并计算1+...+n的值,并存放到resChan // 3. 最后8个协程协同完成工作后,在遍历resChan,显示结果[如 res[1] = 1]...res[10] = 55...] // 4. 注意:考虑resChan chan int是否合适

DatoD的博客 303

Fluent温度超限调试:从能量方程到网格优化的实战指南

本文深入解析Fluent中温度超限问题的诊断与解决方法,从能量方程机制到网格优化策略,提供实战指南。通过分析温度与焓的换算关系,探讨典型场景如能量方程发散、局部网格质量差等,并给出五步调试法及高级命令应用,帮助工程师有效解决温度计算异常问题。

weixin_29045585的博客 531

微电网模型Matlab Simulink,风光储微电网,永磁风机并网仿真,光伏并网仿真,蓄电池仿真

通过探究风光储微电网、永磁风机并网仿真、光伏并网仿真、蓄电池仿真、柴油发电机以及光储微电网和风储微电网等方面的技术,我们可以更好地理解微电网的工作原理和优化方法,从而为微电网的发展做出更大的贡献。微电网模型Matlab Simulink,风光储微电网,永磁风机并网仿真,光伏并网仿真,蓄电池仿真,柴油发电机,光储微电网 风储微电网 Matlab仿真平台搭建的风光储微电网模型,风光柴储微电网,pwm控制,风力发电,光伏发电,微电网并网离网切换,并网孤岛切换,功能强大。永磁风机的并网仿真是一个非常重要的环节。

sjbextz的博客 400

Golang--Go语言 五百行后台代码实现一简约的个人博客网站-TinyBlog

博客演示地址:http://121.36.253.86/,统计了下后台代码只有415行。后续准备专门写篇文章介绍一下和开源共享出来。 当然,一个css或js文件都不可能这么短,这里只是指的后台代码。 支持文章分页,文章分类,按访问量统计,按时间和按点击量排序,最新文章,最热文章,留言评论等功能。使用Golang语言和markdown实现。 整个后台代码就一个main.go 。 运行部署直接go run main.go 或者执行go build 后,执行./main就跑起来了。不用建库,也不用建表

猫哥 的沉淀、积累、总结。天天学习,好好向上...c/c++,嵌入式 linux,Android,HarmonyOS,AIOT) 7929

别再套用星形公式了!三角形接法PMSM的FOC控制,扇区判断与作用时间推导全解析

本文深入解析三角形接法永磁同步电机(PMSM)的FOC控制技术,重点探讨30度相位差对SVPWM扇区判断与电压矢量作用时间的影响。通过数学推导与实验验证,详细讲解修正后的扇区划分规则、作用时间计算公式优化方法,并提供可直接移植到STM32的代码实现,帮助工程师解决三角形接法特有的相电流重构与同步采样难题。

weixin_30689307的博客 471

Golang 控制台百行代码贪吃蛇小游戏

Golang 并不适合在前端工作,缺少强大的图形图像包和硬件加速包,更适合做成后台服务程序。本文的贪吃蛇小游戏运行与控制台上,其中调用了Window系统kernel32.dll中控制台相关的函数。 项目地址: https://gitee.com/redfire0922/snakego 可以使用如下命令下载源码 go get gitee.com/redfire0922/snakego ...

次元代码 3876

Carla运行显示The following component(s) are required to run this program:DirectX Runtime |nvapi64.dll没有被

Carla运行显示The following component(s) are required to run this p 参考: 【1】EPIC 启动游戏提示The following component(s) are required to run this program:... - 『悬赏问答区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cnhttps://www.52pojie.cn/thread-1485651-1-1.html ...

anananajiushiwo的博客 5万+

YOLOv11缝合改进Conv | SAConv在YOLOv11中的应用可切换空洞卷积与性能提升(附代码)

在本篇文章中,我们将深入探讨如何通过引入**SAConv**(Switchable Atrous Convolution,可切换空洞卷积)来优化YOLOv11网络结构,特别是在卷积操作方面的创新。我们会解析SAConv的核心原理和架构设计。

一键难忘的博客 955

Golang提高写入文件性能

goalng文件写入性能

polarrwl的专栏 949

西门子G120变频器在立体仓库堆垛机应用参数设置

本文详细介绍了G120 CU240E及CU250S变频器的参数设置流程。主要内容包括:1)快速调试步骤,从新建项目、扫描节点到设置电机类型、负载类型等参数;2)抱闸参数设置方法;3)参数下载异常时的处理方案。重点说明了通过STARTER软件进行向导式调试的完整过程,包括电机铭牌参数输入、识别方式选择等关键环节,并提示了通讯连接注意事项。当下载失败时,提供了通过导入导出项目文件解决问题的具体方法。

xdpcxq的专栏 864

《编程珠玑》字符串包含

不忘初心,砥砺前行作者 | 陌无崖转载请联系授权字符串包含题目要求解题思路蛮力轮询排序后轮询题目要求给定一个长字符串a和短字符串b,高效判断短字符串b中的所有字符是否都在长字符串中如...

鱼尾九八 181

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Go语言编程笔记1:Hello World

Go语言编程笔记1:Hello World 图源:golang.google.cn 环境安装 Go语言的官网是https://golang.org/,遗憾的是因为某些不可抗力,是没法正常访问的。幸运的是Google提供了一个大陆地区特供版官网,提供一些下载等基本功能,其地址是https://golang.google.cn/dl/。 这里选择对应的版本即可。 具体的安装过程没啥可说的,相当的朴实无华,连个选项都没有,我几乎没有见过类似的程序语言环境安装包。 安装完后同样需要将安装目录加入环境变量Path

梦幻天空 1288

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

双十一,站在巨人的肩膀上囤最优秀的编程书

双十一,囤点什么书呢?小编想了想,新书?经典书?畅销书?感觉似乎每一类都值得推荐 。每一年出版社的专业新书上百本,如何在适宜的时间选您需要的好书?双十一就是一个很好的时间选择。让小编带你浏览本年度最值得推荐的双十一好书。 经典书必备(思维修炼必备) 1、编程珠玑(第2版) 本书是计算机科学方面的经典名著。书的内容围绕程序设计人员面对的一系列实际问题展开。作者Jon Bentley 以其独有的洞察力和创造力,引导读者理解这些问题并学会解决方法,而这些正是程序员实际编程生涯中至关重要的。本书的特.

人邮异步社区 7204

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

golang的BitSet

1.BitSet是什么 BitSet是借鉴位图(BitMap)概念创造的一个数据结构。所谓的BitMap 就是用一个 bit 位来标记某个元素对应的 Value,而 Key 即是该元素。由于采用了 Bit 为单位来存储数据,因此在内存占用方面,可以大大节省。(《编程珠玑》第一章引入的问题,提到了 Bitmap) BitSet的好处是用很小的空间标记已存的数据(每一bit对应)。 在查找中,我...

wangzhezhilu001的专栏 1695

给大家推荐几本最优秀的编程书

现在给大家推荐几本最优秀的编程书 一、《编程珠玑》(第2版) 本书是计算机科学方面的经典名著。书的内容围绕程序设计人员面对的一系列实际问题展开。作者Jon Bentley 以其独有的洞察力和创造力,引导读者理解这些问题并学会解决方法,而这些正是程序员实际编程生涯中至关重要的。本书的特色是通过一些精心设计的有趣而又颇具指导意义的程序,对实用程序设计技巧及基本设计原则进行了透彻而睿智的描述,为复杂的编程问题提供了清晰而完备的解决思路。本书对各个层次的程序员都具有很高的阅读价值。 二、《数学之美》 第三版 这

guoxuying的博客 9272

面试题 16.15. 珠玑妙算

本文旨在对于个人知识的梳理以及知识的分享,如果有不足的地方,欢迎大家在评论区指出 题目描述 珠玑妙算游戏(the game of master mind)的玩法如下。 计算机有4个槽,每个槽放一个球,颜色可能是红色(R)、黄色(Y)、绿色(G)或蓝色(B)。例如,计算机可能有RGGB 4种(槽1为红色,槽2、3为绿色,槽4为蓝色)。作为用户,你试图猜出颜色组合。打个比方,你可能会猜YRGB。要是猜对某个槽的颜色,则算一次“猜中”;要是只猜对颜色但槽位猜错了,则算一次“伪猜中”。注意,“猜中”不能算入“伪猜

qq_43267227的博客 340

我的编程书单

我的编程书单 今天将我豆瓣上的编程书单整理了一下,添加上了对已读数目的简介,或许可以对部分网友起到一些帮助。 说明 这是我的编程书单。在大学期间关于编程书籍的选取上,我最大的感悟是:在选择编程书籍或者学习资料的时候,不要盲目相信权威或者资深开发者,一定要真正阅读部分内容后自行判断该书籍是否适合自己,并不存在某本书籍适合所有人学习这种情况。同样的,也并非所有人都适合同样的学习方式,比如我个人就比较喜欢读纸质书,而非电子版或者看视频,着同样是一个因人而异的事情,所以以下仅供参考,请选择适合自己的方式进行学习。

梦幻天空 522

整理了一些免费的编程中文书籍

在 github 上整理了一些免费的编程中文书籍 https://github.com/justjavac/free-programming-books-zh_CN 如果谁还有别的书籍,可在此留言。 语言无关类 WEB服务器 Nginx开发从入门到精通 (源码) 版本控制 Git 参考手册 (源码)Pro Git (源码) NoSQL The Lit

justjavac(迷渡) 8598

评点2021-2022年上市的那些Go语言新书

计算机科学与技术这个工业大类与传统工业类别相比还很“年轻”,并且由于历史原因,整个计算机科学与技术学科的奠基都是由欧美人完成的,因此但凡诞生一门新IT技术或新编程语言,我们首先参考的都是来自欧美的外文技术书籍(影印或翻译)。以Go为例,笔者最先接触的Go技术书籍资料是《The Way To Go》[1]:这也是笔者早期学习Go语言时最喜欢翻看的一本书,也是我目前见到的、最...

TonyBai 2191

golang 实现bitmap

一、概述 本文将讲述Bit-Map算法的相关原理,Bit-Map算法的一些利用场景,例如BitMap解决海量数据寻找重复、判断个别元素是否在海量数据当中等问题.最后说说BitMap的特点已经在各个场景的使用性。 二、Bit-Map算法 先看看这样的一个场景(来自《编程珠玑》):给一台普通PC,2G内存,要求处理一个包含40亿个不重复并且没有排过序的无符号的int整数,给出一个整数,问如果快速...

diu_brother的博客 2666

golang实现位图(BitSet)

一, 概念      Bitmap (位图)是一个十分有用的数据结构。所谓的 Bit-map 就是用一个 bit 位来标记某个元素对应的 Value,而 Key 即是该元素。由于采用了 Bit 为单位来存储数据,因此在内存占用方面,可以大大节省。(《编程珠玑》第一章引入的问题,提到了 Bitmap) 二, 实现基本原理      类似于java的BitSet, 是位操作的对象

kdpujie的专栏 4689

五一小长假,这份书单要不要收下?

转眼明天就是五一啦,小长假期间很多朋友有了出行计划,有的打算去旅游,有的打算探亲。然而正是因为黄金周,大家都会集中在这几天出行,所以一定要注意安全。保管好随身携带财物,留意天气变化,做好出行准备。 放个小长假,小编也要唠叨一下书单,放几本轻松的,再放几本充电的,祝您五一小长假假期愉快!! 休闲阅读 想提升自身修养看下这两本: 重构:改善既有代码的设计(第2版) [美] 马丁·福勒(Marti...

人邮异步社区 847

码农群英传连载(一) —— Go 语言之父 Rob Pike

近几年来在计算机语言排行榜上成长最快的语言就是 Go 语言。它勇往直前势如破竹,从几年前的 50 名之外快速跃进前十的宝座,而且还在马不停蹄向上攀登。大家都知道 Go 语...

shellquery的博客 132
上一篇: 您有一个 MatrixOne Beta Program 专属权益待领取
下一篇: MatrixOne 0.7.0: 更稳定,性能更优
MatrixOrigin
博客等级 码龄5年 708粉丝 230原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值