(cppcon2020) back to basics

Algebraic Data Types

指 pair tuple (product type)(结构体) optional variant(sum type) (有index信息)

std::any不行,有类型擦除,丢信息了

pair tuple 多种信息

  • std::in_place/std::piecewise_construct
  • forward_as_tuple
  • std::tie
    • 用std::tie来实现比较
  • 结构化绑定
  • 公共接口还是定义一个类/结构体吧,丢失了名字信息,很可惜

optional,优雅,不浪费堆空间

optional 默认值, 没有值 对于指针场景,没有堆使用。优雅

std::unique_ptr<ComplicatedObject> obj_ = nullptr;
void setComplicated(int a, int b) {
	obj_ = std::make_unique<ComplicatedObject>(a, b);
}

std::optional<ComplicatedObject> obj_ = std::nullopt;
void setComplicated(int a, int b) {
	obj_.emplace(a, b);
}
  • std::optional<int> o = std::nullopt
  • value_or方法,非常优雅,省一个if
    • 必须是constexpr的,不然or不了
  • Setter, 用(std::optional) const std::optional<T\>&会拿到个临时对象?

variant 优雅的union

index方法 返回下表

std::get可以 通过index和类型来访问 类似的std::get_if

std::visit

poor man’s Expected<T>.

std::variant<std::string, std::errc> vGetenv(const char *name);
if (auto v = vGetenv("foo"); std::get_if<std::string>(&v)) {
	const auto& value = std::get<std::string>(v);
	std::cout << "Value is: " << value << "\n";
} else {
	std::error_condition error = std::get<std::errc>(v);
	std::cout << "Error was: " << error.message() << "\n";
}

Class Layout

  • 静态变量,非虚成员函数,静态成员函数,类型成员不会影响类的存储布局

  • 空基类,[[no_unique_address]]修饰,强制指定不占用空间

    • 空基类优化暂且不提,因为依赖编译器是否做优化。
  • 比较,auto operator <=>(const Flatland &) const = default;

    • 别默认内存布局memcmp,可能会失败(什么时候会失败????)
  • POD,以及pack

    • is_standard_layout_v
    class NarrowLand {
        unsigned char x;       // offset 0
        unsigned long long y;  // offset 8 (still!)
        unsigned long long z;  // offset 16
        friend bool operator ==(NarrowLand const &lhs, NarrowLand const &rhs);
    };
    bool operator ==(NarrowLand const &lhs, NarrowLand const &rhs) {
    	if constexpr (has_unique_object_representations_v<NarrowLand>)
    		return !memcmp(&lhs, &rhs, sizeof(NarrowLand));
    	else
    		return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
    }
    
  • vptr

    • dynamic_cast 开销大 static_cast有偏移
  • 介绍了一波实现。太累了。不看了。这段东西看着就头疼


Concurrency

What is a data race and how do we fix it?

  • The hardware can reorder accesses 指令重排
  • ABA
    • busy-wait aka spinning
    • std::mutex
      • exception-safety? RAII
  • condition_variable for “wait until” 生产消费
    • produce/consume happen only once, consider std::promise/std::future 实际上内部也是mutex+cv
struct TokenPool {
	std::vector<Token> tokens_;
	std::mutex mtx_;
	std::condition_variable cv_;
	void returnToken(Token t) {
		std::unique_lock lk(mtx_);
		tokens_.push_back(t);
		lk.unlock();//!
		cv_.notify_one();
	}
	Token getToken() {
		std::unique_lock lk(mtx_);
		while (tokens_.empty()) {
			cv_.wait(lk);
		}
		Token t = std::move(tokens_.back());
		tokens_.pop_back();
		return t;
	}
};

Static initialization and once_flag 多线程的初始化

class Logger {
	std::mutex mtx_;
	std::optional<NetworkConnection> conn_;
	NetworkConnection& getConn() {
		std::lock_guard<std::mutex> lk(mtx_);
		if (!conn_.has_value()) {
			conn_ = NetworkConnection(defaultHost);
		}
		return *conn_;
	}
};

class Logger {
	std::once_flag once_;
	std::optional<NetworkConnection> conn_;
	NetworkConnection& getConn() {
		std::call_once(once_, []() {
			conn_ = NetworkConnection(defaultHost);
		});
		return *conn_;
	}
};
mutex condition_variable once_flag
lock blocks only if someone “owns” the mutex. wait always blocks. call_once blocks only if the “done” flag isn’t yet set.
Many threads can queue up on lock. Many threads can queue up on wait. Many threads can queue up on call_once.
Calling unlock unblocks exactly one waiter: the new “owner.” Calling notify_one unblocks exactly one waiter. Failing at the callback unblocks exactly one waiter: the new “owner.”
  Calling notify_all unblocks all waiters. Succeeding at the callback unblocks all waiters and sets the “done” flag.

New C++17 and C++20 primitives

  • shared_mutex
  • counting_semaphore
using Sem = std::counting_semaphore<256>;
struct SemReleaser {
	bool operator()(Sem *s) const { s->release(); }
};
class AnonymousTokenPool {
	Sem sem_{100};
	using Token = std::unique_ptr<Sem, SemReleaser>;
	Token borrowToken() {
		sem_.acquire(); // may block
		return Token(&sem_);
	}
};
  • std::latch
  • std::barrier<>

image-20200923190229383

Patterns for sharing data

  • Remember: Protect shared data with a mutex.
    • You must protect every access, both reads and writes, to avoid UB.
    • Maybe use a reader-writer lock (std::shared_mutex) for perf.
  • Remember: Producer/consumer? Use mutex + condition_variable.

  • Best of all, though: Avoid sharing mutable data between threads.
    • Make the data immutable.
    • Clone a “working copy” for yourself, mutate that copy, and then quickly “merge” your changes back into the original when you’re done

In conclusion

  • Unprotected data races are UB
    • Use std::mutex to protect all accesses (both reads and writes)
  • Thread-safe static initialization is your friend
    • Use std::once_flag only when the initializee is non-static
  • mutex + condition_variable are best friends

  • C++20 gives us “counting” primitives like semaphore and latch

  • But if your program is fundamentally multithreaded, look for higher-level facilities: promise/future, coroutines, ASIO, TBB

  • std::atomic_ref<T>
  • std::jthread

Exceptions

  • 异常带来的开销大于错误的影响
    • 解决方案 std::expected<T, E>
  • 异常使得函数难以理解
  • 异常依赖动态库
  • 异常加大二进制大小
  • 什么时候使用/不用异常
    • 不经常发生的错误
      • 经常出错,出错属于正常场景,别用
    • 异常不能处理的场景
      • IO错误
    • 构造函数等等不应该出错的场景
      • 引用空指针,越界等等应该保证不出错
  • 异常安全保证
      • All functions should at least provide the basic exception safety guarantee, if possible and reasonable the strong guarantee.
      • Consider the no-throw guarantee, but only provide it if you can guarantee it even for possible future changes.
    • 基本的异常安全保证
      • 没有资源泄漏
      • Invariants are preserved ?
    • 强异常安全保证
      • Invariants are preserved
      • 没有资源泄漏
      • 状态未改变 commit-or-rollback
    • 不抛异常
      • 操作不能失败
      • noexcept
  • RAII RAII*is the single most important idiom of the C++ programming language. Use it!
  • 不能失败
  • 析构函数
    • stack unwinding
    • 失败就terminate了
    • 默认noexcept
    • 清理必须安全
  • move 操作符
    • Core Guideline C.66: Make move operations noexcept
  • swap操作符,由基本的操作实现,不会失败

Lambda Expressions

c++20

[capture clause] <template parameters\> (parameter list)
specifier exception attribute -> return type requires { body }
  • specifier
    • mutable
    • constexpr(能推导出来,所以这个非必须)
    • consteval
  • exception
    • noexcept
    • throw 别用
  • requires
    • capture clause
    • template parameters
    • arguments passed in the parameter list
    • anything which can be checked at compile time
  • capture std::unique_ptr
std::unique_ptr<Widget> myPtr = std::make_unique<Widget>();
auto myLamb = [ capturedPtr = std::move(myPtr) ] ( )
{ return capturedPtr->computeSize(); };

Move Semantics

再谈右值

  • No rvalue reference as function return type
int&& func() { return 42; }
void test() {
	int a = func();//返回之前已经销毁
}

std::move

template <class T>
constexpr remove_reference_t<T>&& move(T&& t) noexcept
{
	return static_cast<remove_reference_t<T>&&>(t);
}
  • Next operation after std::move is destruction or assignment move完只能销毁或者重新赋值,其他操作会引入问题
  • Don’t std::move the return of a local variable 别move返回值

move ctor

  • Move constructor / assignment should be explicitly noexcept
  • Use t =default when possible
  • Moved-from object must be left in a valid state
  • Make move assignment safe for self-assignment
struct S {
	double* data;
	S( S&& other ) noexcept
		: data(std::exchange(other.data, nullptr))
	{ }
    
    S& operator=( S&& other ) noexcept {
        if (this == &other) return *this;
		
        delete[] data;
		data = std::exchange(other.data, nullptr);
		return *this;
	}
};

完美转发

template <class T> void f(T&& value)
{
	g(std::forward<T>(value));
}

有些类型只能move不能copy


Smart Pointers

  • std::unique_ptr
    • 没有copy语义
    • 比raw pointer无劣势
    • 定制deleter(需要可见)
    • 数组的特化std::unique_ptr<T[]>
    • std::make_unique 要比std::unique_ptr构造要快
  • std::shared_ptr
    • 有count原子计数。有消耗
      • 本身线程安全但是不保证引用的资源是线程安全
    • 定制deleter
    • std::make_shared 要比std::shared_ptr构造要快
    • std::shared_ptr<void>
      • https://www.cnblogs.com/imjustice/p/how_shared_ptr_void_works.html
      • https://stackoverflow.com/questions/5913396/why-do-stdshared-ptrvoid-work
  • std::weak_ptr
    • 借,生成shared_ptr

使用建议,最好别用share or

  • std::atomic_shared_ptr/std::atomic_weak_ptr -> std::atomic<std::shared_ptr<T>> std::atomic<std::weak_ptr<T>>

C++ Templates

  • c++17引入CTAD 没啥说的

  • using

template<size_t N>
using CharArray = std::array<char, N>;
  • std::array受限于NTTP这种参数难受,不如std::span

  • 变参模板

  • SFINAE

    • 其他套路
      • tag dispatch
      • if constexpr
    • c++20 用concept代替

Abstract Machines/The Structure of a Program

讲了一遍编译原理

  • ODR

  • ABI

  • name-mangling

  • 变量存储在哪

    • 注意static和thread_local

ref

  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_algebraic_data_types/back_to_basics_algebraic_data_types__arthur_odwyer__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_class_layout/back_to_basics_class_layout__steve_dewhurst__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_concurrency/back_to_basics_concurrency__arthur_odwyer__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_exceptions/back_to_basics_exceptions__klaus_iglberger__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_lambda_expressions/back_to_basics_lambda_expressions__barbara_geller__ansel_sermersheim__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_move_semantics/back_to_basics_move_semantics__david_olsen__cppcon_2020.pdf
    • Nicolai M. Josuttis, C++ Move Semantics: The Complete Guide,http://www.cppmove.com/
    • C++ Core Guidelineshttps://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html
    • Nicolai Josuttis, “The Hidden Secrets of Move Semantics”, CppCon 2020
    • Nicolai Josuttis, “The Nightmare of Move Semantics for Trivial Classes”, CppCon 2017 https://www.youtube.com/watch?v=PNRju6_yn3o
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_smart_pointers/back_to_basics_smart_pointers__rainer_grimm__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_templates_part_1/back_to_basics_templates_part_1__andreas_fertig__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_templates_part_2/back_to_basics_templates_part_2__andreas_fertig__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_the_abstract_machine/back_to_basics_the_abstract_machine__bob_steagall__cppcon_2020.pdf
  • https://github.com/CppCon/CppCon2020/blob/main/Presentations/back_to_basics_the_structure_of_a_program/back_to_basics_the_structure_of_a_program__bob_steagall__cppcon_2020.pdf

看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

(转)boost.asio新框架的设计概念总结

1.66版本,boost.asio库重新设计了框架,目前最新版为1.71。读了几天代码后,对框架中相关概念总结。因为是泛型编程的库,所以分析的概念层的设计。

可通过boost官方文档,strand的1.65和1.66两版本文档比较,查证ts和io_context, executor首次出现在1.66。

新框架有几个核心概念,Context,Scheduler,Service,Executor,Strand。

Context:

  • asio所有功能都必需在一个*Context* 里调度执行
  • 每个Context 都有一个Service 注册表,管理Service
  • 每个Context 下的Service 都是唯一的
  • 每个Context 都有一个Scheduler
  • Context 必须通过在线程运行poll()或run()进入调度消费Scheduler 执行队列并执行任务
  • io_context是一种对io操作优先的优化Context,将io事件复路分集方法做成内嵌任务
  • io_context的win版本对Schdeluer 进行了优化,聚合了iocp。
  • 可以在多线程上同时运行poll()或run(),并且线程安全

Scheduler:

  • 首先是一个Context 的一个服务
  • 有一条op_queue执行队列
  • 所有Service 的调度都最终依赖Scheduler 调度
  • Scheduler 的dispatch()方法将任务调度到执行队列

Service:

  • 为某种功能提供调度以及功能服务
  • 最终依赖所在的 ContextScheduler 调度服务
  • 每种 Service 都有一个service_impl类,并为这个类提供服务

Executor:

  • 相当于ios中的可并行的dispatch_queue
  • 相当于一个 Context 的服务,或者对 ContextExecution 行为的委托
  • 最终依赖所在的ContextScheduler调度服务

Strand:

  • 相当于ios中的串行化的dispatch_queue
  • 分两种服务,绑定本io Context 以及可以指定Executor (即不同类型Context)
  • 每个Strand 有独立的执行队列
  • Strand 本身作为一个任务,必须在Scheduler 进行调度分派。
  • 同一个Strand 同时只能在一条线程上分派执行队列
  • 当多线程同时对Strand 分派时,其它线程只能将任务缓冲到等待队列
  • 利用本身强制串行化的特性,可代替同步锁,保护变量和代码,减少线程切换

ref

  • https://www.cnblogs.com/bbqzsl/p/11919502.html
  • asio使用样例,不错 https://github.com/franktea/network
  • 介绍实现的 https://zhuanlan.zhihu.com/p/55503053
  • http://spiritsaway.info/asio-implementation.html#f69817

看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

go源码剖析笔记

环境

go version #go version go1.10.4 linux/amd64
lsb_release -d #Description:    Ubuntu 18.04.1 LTS
gdb --version #GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git

引导

测试代码 test.go

package main
func main() {
    println("hello, world");
}
go build -gcflags "-N -l" -o test test.go
gdb test
(gdb) info files
Symbols from "/mnt/c/Program Files/cmder/test".
Local exec file:
        `/mnt/c/Program Files/cmder/test', file type elf64-x86-64.
        Entry point: 0x4477c0
        0x0000000000401000 - 0x000000000044c213 is .text
        0x000000000044d000 - 0x00000000004757a3 is .rodata
        0x00000000004758e0 - 0x0000000000475f80 is .typelink
        0x0000000000475f80 - 0x0000000000475f88 is .itablink
        0x0000000000475f88 - 0x0000000000475f88 is .gosymtab
        0x0000000000475fa0 - 0x00000000004a3630 is .gopclntab
        0x00000000004a4000 - 0x00000000004a4a08 is .noptrdata
        0x00000000004a4a20 - 0x00000000004a65b0 is .data
        0x00000000004a65c0 - 0x00000000004c2888 is .bss
        0x00000000004c28a0 - 0x00000000004c4e58 is .noptrbss
        0x0000000000400f9c - 0x0000000000401000 is .note.go.buildid
(gdb) b *0x4477c0
Breakpoint 1 at 0x4477c0: file /usr/lib/go-1.10/src/runtime/rt0_linux_amd64.s, line 8.

版本对应的汇编有变化,没有明显的main,但是入口肯定是_rt0_amd64

#include "textflag.h"

TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
        JMP     _rt0_amd64(SB)

TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0
        JMP     _rt0_amd64_lib(SB)
        
        
  
(gdb) b _rt0_amd64
Breakpoint 2 at 0x444100: file /usr/lib/go-1.10/src/runtime/asm_amd64.s, line 15.

对应汇编是书里的runtime.rt0_go

TEXT _rt0_amd64(SB),NOSPLIT,$-8
        MOVQ    0(SP), DI       // argc
        LEAQ    8(SP), SI       // argv
        JMP     runtime·rt0_go(SB)
b runtime.rt0_go
Breakpoint 3 at 0x444110: file /usr/lib/go-1.10/src/runtime/asm_amd64.s, line 89.
       ;前面有很多对于汇编指令cpu类型的判断,参数入栈等等
       // create a new goroutine to start program
        MOVQ    $runtime·mainPC(SB), AX                // entry
        PUSHQ   AX
        PUSHQ   $0                      // arg size
        CALL    runtime·newproc(SB)
        POPQ    AX
        POPQ    AX

        // start this M
        CALL    runtime·mstart(SB)

        MOVL    $0xf1, 0xf1  // crash
        RET

DATA    runtime·mainPC+0(SB)/8,$runtime·main(SB)
GLOBL   runtime·mainPC(SB),RODATA,$8
b runtime.schedinit
Breakpoint 6 at 0x423a60: file /usr/lib/go-1.10/src/runtime/proc.go, line 477.
b runtime.main
Breakpoint 4 at 0x4228b0: file /usr/lib/go-1.10/src/runtime/proc.go, line 109.

schedinit 入口

// The bootstrap sequence is:
//
//      call osinit
//      call schedinit
//      make & queue new G
//      call runtime·mstart
//
// The new G calls runtime·main.
func schedinit() {
        // raceinit must be the first call to race detector.
        // In particular, it must be done before mallocinit below calls racemapshadow.
        _g_ := getg()
        if raceenabled {
                _g_.racectx, raceprocctx0 = raceinit()
        }

        sched.maxmcount = 10000

        tracebackinit()
        moduledataverify()
        stackinit()
        mallocinit()
        mcommoninit(_g_.m)
        alginit()       // maps must not be used before this call
        modulesinit()   // provides activeModules
        typelinksinit() // uses maps, activeModules
        itabsinit()     // uses activeModules
        
        msigsave(_g_.m)
        initSigmask = _g_.m.sigmask

        goargs()
        goenvs()
        //处理GODEBUG GOTRACEBACK宏
        parsedebugvars()
        //垃圾回收器初始化
        gcinit()

        sched.lastpoll = uint64(nanotime())
        //通过CPU core和GOMAXPROCS确定P数量
        procs := ncpu
        if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
                procs = n
        }
        // 调整P数量
        if procresize(procs) != nil {
                throw("unknown runnable goroutine during bootstrap")
        }

        // For cgocheck > 1, we turn on the write barrier at all times
        // and check all pointer writes. We can't do this until after
        // procresize because the write barrier needs a P.
        if debug.cgocheck > 1 {
                writeBarrier.cgo = true
                writeBarrier.enabled = true
                for _, p := range allp {
                        p.wbBuf.reset()
                }
        }


下一步是runtime.main

// The main goroutine.
func main() {
        g := getg()

        // Racectx of m0->g0 is used only as the parent of the main goroutine.
        // It must not be used for anything else.
        g.m.g0.racectx = 0

        // Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
        // Using decimal instead of binary GB and MB because
        // they look nicer in the stack overflow failure message.
        if sys.PtrSize == 8 {
                maxstacksize = 1000000000
        } else {
                maxstacksize = 250000000
        }

        // Allow newproc to start new Ms.
        //启动系统后台监控/定期垃圾回收,并发任务调度相关
        mainStarted = true
        systemstack(func() {
                newm(sysmon, nil)
        })

        // Lock the main goroutine onto this, the main OS thread,
        // during initialization. Most programs won't care, but a few
        // do require certain calls to be made by the main thread.
        // Those can arrange for main.main to run in the main thread
        // by calling runtime.LockOSThread during initialization
        // to preserve the lock.
        lockOSThread()

        if g.m != &m0 {
                throw("runtime.main not on m0")
        }

        runtime_init() // must be before defer
        if nanotime() == 0 {
                throw("nanotime returning zero")
        }

        // Defer unlock so that runtime.Goexit during init does the unlock too.
        needUnlock := true
        defer func() {
                if needUnlock {
                        unlockOSThread()
                }
        }()
            // Record when the world started. Must be after runtime_init
        // because nanotime on some platforms depends on startNano.
        runtimeInitTime = nanotime()

        gcenable()

        main_init_done = make(chan bool)
        if iscgo {
                if _cgo_thread_start == nil {
                        throw("_cgo_thread_start missing")
                }
                if GOOS != "windows" {
                        if _cgo_setenv == nil {
                                throw("_cgo_setenv missing")
                        }
                        if _cgo_unsetenv == nil {
                                throw("_cgo_unsetenv missing")
                        }
                }
                if _cgo_notify_runtime_init_done == nil {
                        throw("_cgo_notify_runtime_init_done missing")
                }
                // Start the template thread in case we enter Go from
                // a C-created thread and need to create a new thread.
                startTemplateThread()
                cgocall(_cgo_notify_runtime_init_done, nil)
        }

        fn := main_init // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
        fn()
        close(main_init_done)

        needUnlock = false
        unlockOSThread()

        if isarchive || islibrary {
                // A program compiled with -buildmode=c-archive or c-shared
                // has a main, but it is not executed.
                return
        }
        fn = main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
        fn()
        if raceenabled {
                racefini()
        }
        // Make racy client program work: if panicking on
        // another goroutine at the same time as main returns,
        // let the other goroutine finish printing the panic trace.
        // Once it does, it will exit. See issues 3934 and 20018.
        if atomic.Load(&runningPanicDefers) != 0 {
                // Running deferred functions should not take long.
                for c := 0; c < 1000; c++ {
                        if atomic.Load(&runningPanicDefers) == 0 {
                                break
                        }
                        Gosched()
                }
        }
        if atomic.Load(&panicking) != 0 {
                gopark(nil, nil, "panicwait", traceEvGoStop, 1)
        }

        exit(0)
        //? 这啥
        for {
                var x *int32
                *x = 0
        }

一个复杂示例

//cat lib/sum.go
package lib
func init() {
    println("sum.init")
}

func Sum(x ...int) int {
    n  := 0
    for _, i := range x{
        n += i
    }
    return n
}
//cat test.go
package main
import (
    "./lib"
)
func init() {
    println("test.init")
}

func test() {
    println(lib.Sum(1,2,3))
}

//cat main.go
package main

import (
        _ "net/http"
)

func init() {
    println("main.init.2")
}

func main() {
    test()
}

func init() {
    println("main.init.1")
}

执行结果

go build -gcflags "-N -l" -o test
./test
sum.init
main.init.2
main.init.1
test.init
6

查看反汇编

;go tool objdump -s "runtime\.init\b" test
TEXT runtime.init.0(SB) /usr/lib/go-1.10/src/runtime/cpuflags_amd64.go
TEXT runtime.init.1(SB) /usr/lib/go-1.10/src/runtime/mgcwork.go
  mgcwork.go:25         0x420860                c3                      RET
TEXT runtime.init.2(SB) /usr/lib/go-1.10/src/runtime/mstats.go
  mstats.go:438         0x4260d0                64488b0c25f8ffffff      MOVQ 
TEXT runtime.init.3(SB) /usr/lib/go-1.10/src/runtime/panic.go
TEXT runtime.init.4(SB) /usr/lib/go-1.10/src/runtime/proc.go
TEXT runtime.init.5(SB) /usr/lib/go-1.10/src/runtime/signal_unix.go
  signal_unix.go:64     0x43e450                c3                      RET
TEXT runtime.init(SB) <autogenerated>


;go tool objdump -s "main\.init\b" test
TEXT main.init.0(SB) /mnt/c/Program Files/cmder/main.go
TEXT main.init.1(SB) /mnt/c/Program Files/cmder/main.go
TEXT main.init.2(SB) /mnt/c/Program Files/cmder/test.go
TEXT main.init(SB) <autogenerated>
 <autogenerated>:1     0x5e31ec                e81f63ffff              CALL net/http.init(SB)

  <autogenerated>:1     0x5e31f1                e83afdffff              CALL _/mnt/c/Program_Files/cmder/lib.init(SB)
  <autogenerated>:1     0x5e31f6                e895fdffff              CALL main.init.0(SB)

  <autogenerated>:1     0x5e31fb                e820feffff              CALL main.init.1(SB)

  <autogenerated>:1     0x5e3200                e87bfeffff              CALL main.init.2(SB)

  <autogenerated>:1     0x5e3205                c605822a1f0002          MOVB $0x2, main.initdone.(SB)

  <autogenerated>:1     0x5e320c                488b2c24                MOVQ 0(SP), BP

  <autogenerated>:1     0x5e3210                4883c408                ADDQ $0x8, SP

  <autogenerated>:1     0x5e3214                c3                      RET

  <autogenerated>:1     0x5e3215                e80600e7ff              CALL runtime.morestack_noctxt(SB)

  <autogenerated>:1     0x5e321a                eb84                    JMP main.init(SB)

结论

所有init都会在同一个goroutine执行

所有init函数结束后才会执行main.main

内存分配

基本策略

  • 每次从操作系统申请一大块内存,减少系统调用
  • 内存分配器
    • 大块内存预先切成小块构成链表
    • 分配就从链表里提取一块
    • 回收旧放回链表
    • 空闲过多会归还给系统降低整体开销

内存块

  • span page 大块内存
  • object切分span多个小块
  • 哦,抄的tcmalloc

初始化动作

三个数组组成内存管理结构

  • spans,管理span的,按页对应,地址按页对齐能快速定位(?这里的原理不太清楚,我对页这些东西计算一直处于一知半解水平)
  • bitmap 为每个对象提供4bit标记为,保存指针,GC标记
  • arena 申请内存,用户可分配上限

arena和spans bitmap存在映射关系,三者可以按需同步线性扩张

都用mheap维护,在mallocinit里初始化

来个示例

//test.go
package main

import(
    "fmt"
    "os"
    "github.com/shirou/gosutil/process"
)

var ps *process.Process

func mem(n int) {
    if ps == nil {
            p, err := process.NewProcess(int32(os.Getpid()))
                if err != nil {
                    panic(err)
                }

                ps = p
        }

        mem, _ := ps.MemoryInfoEx()
        fmt.Printf("%d, VMS:%d MB, RSS:%d MB\n", n, mem,.VMS>>20, mem.RSS>>20)
}

func main(){
    mem(1)
        data : new([10][1024*1024]byte)
    mem(2)

    for i := range data {
            for x, n := 0, len(data[i]); x<n; x++ {
                    data[i][x] = 1
                }
                mem(3)
        }
}

分配

不要以为new一定会分配在堆上,随着优化内联

package main
import ()
func test() *int {
    x := new(int)
    *x = 0xAABB
    return x
}
func main() {
    println(*test())
}
go build -gcflags "-l" -o test test.go
go tool objdump -s "main\.test" test
TEXT main.test(SB) /mnt/c/Program Files/cmder/test.go
  test.go:4             0x44c150                64488b0c25f8ffffff      MOVQ FS:0xfffffff8, CX
  test.go:4             0x44c159                483b6110                CMPQ 0x10(CX), SP
  test.go:4             0x44c15d                7639                    JBE 0x44c198
  test.go:4             0x44c15f                4883ec18                SUBQ $0x18, SP
  test.go:4             0x44c163                48896c2410              MOVQ BP, 0x10(SP)
  test.go:4             0x44c168                488d6c2410              LEAQ 0x10(SP), BP
  test.go:5             0x44c16d                488d05acac0000          LEAQ 0xacac(IP), AX
  test.go:5             0x44c174                48890424                MOVQ AX, 0(SP)
  test.go:5             0x44c178                e8a3effbff              CALL runtime.newobject(SB)
  test.go:5             0x44c17d                488b442408              MOVQ 0x8(SP), AX
  test.go:6             0x44c182                48c700bbaa0000          MOVQ $0xaabb, 0(AX)
  test.go:7             0x44c189                4889442420              MOVQ AX, 0x20(SP)
  test.go:7             0x44c18e                488b6c2410              MOVQ 0x10(SP), BP
  test.go:7             0x44c193                4883c418                ADDQ $0x18, SP
  test.go:7             0x44c197                c3                      RET
  test.go:4             0x44c198                e8d383ffff              CALL runtime.morestack_noctxt(SB)
  test.go:4             0x44c19d                ebb1                    JMP main.test(SB)
  


go build -o test test.go
go tool objdump -s "main\.main" test
TEXT main.main(SB) /mnt/c/Program Files/cmder/test.go
  test.go:10            0x44c150                64488b0c25f8ffffff      MOVQ FS:0xfffffff8, CX
  test.go:10            0x44c159                483b6110                CMPQ 0x10(CX), SP
  test.go:10            0x44c15d                7634                    JBE 0x44c193
  test.go:10            0x44c15f                4883ec10                SUBQ $0x10, SP
  test.go:10            0x44c163                48896c2408              MOVQ BP, 0x8(SP)
  test.go:10            0x44c168                488d6c2408              LEAQ 0x8(SP), BP
  test.go:11            0x44c16d                e88e59fdff              CALL runtime.printlock(SB)
  test.go:11            0x44c172                48c70424bbaa0000        MOVQ $0xaabb, 0(SP)
  test.go:11            0x44c17a                e80161fdff              CALL runtime.printint(SB)
  test.go:11            0x44c17f                e80c5cfdff              CALL runtime.printnl(SB)
  test.go:11            0x44c184                e8f759fdff              CALL runtime.printunlock(SB)
  test.go:12            0x44c189                488b6c2408              MOVQ 0x8(SP), BP
  test.go:12            0x44c18e                4883c410                ADDQ $0x10, SP
  test.go:12            0x44c192                c3                      RET
  test.go:10            0x44c193                e8d883ffff              CALL runtime.morestack_noctxt(SB)
  test.go:10            0x44c198                ebb6                    JMP main.main(SB)

逃逸分析-gcflag “-m”

分配思路 malloc.go

  • 大对象heap
  • 小对象cache.alloc[sizeclass].freelist object
  • 微小对象使用cache.tiny object

回收

回收以span为单位

释放

sysmon监控任务来搞

具体释放是madvie(v, n, _MADV_DONTNEED) 系统来决定。如果物理内存资源充足,就不会回收避免无谓的损耗,不过再次使用肯定会pagefault然后分配新的内存

垃圾回收

缩短STW时间

抑制堆增长 充分利用CPU资源

  • 三色标记和写屏障
    • 所有都是白色
    • 扫描出所有可达对象,标记成灰色,放出待处理队列
    • 队列提取出灰色对象,将其引用对象标记为灰色放入队列,自身标记为黑色
    • 写屏障监视对象内崔修改,重新标色或放回队列

gcController控制

辅助回收,避免分配速度大于后台标记导致的堆恶性扩张

ref


看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

八月待读 need review

公司不让访问外网了呜呜

https://support.hypernode.com/en/troubleshooting/performance/how-to-debug-out-of-memory-oom-events

https://briancallahan.net/blog/20200816.html

https://lobste.rs/s/2vj4sb/file_handling_unix_tips_traps_outright

https://danluu.com/file-consistency/

https://rachelbythebay.com/w/2020/08/11/files/

https://robertovitillo.com/what-every-developer-should-know-about-database-consistency/

http://brooker.co.za/blog/2020/05/25/reading.html

https://codahale.com/work-is-work/

https://bartoszmilewski.com/2020/08/11/benign-data-races-considered-harmful/

https://www.fluentcpp.com/2020/06/12/a-generic-component-for-out-of-line-lambdas/

http://brooker.co.za/blog/2020/03/22/rust.html

https://briancallahan.net/blog/20200812.html

https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing/

https://zhuanlan.zhihu.com/p/108968057

https://www.cockroachlabs.com/blog/cockroachdb-sigmod-2020/?utm_campaign=cooperpress-2020-Q2&utm_source=dbweekly&utm_medium=newsletter&utm_content=dbweekly-primary-sigmod

https://ketanbhatt.com/db-concurrency-defects/

看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

(译)Data Structures Part 1 Bulk Data

作者把数据结构抽象了,不按照传统的什么树跳表堆什么的,根据用途来分类

  • Bulk Data,也就是保存大量对象
  • 弱引用/handle 对于Bulk Data的引用,即使对象没了也不会访问崩溃
  • indices索引,访问特定Bulk Data的索引下表
  • 数组的数组,保存一大堆Bulk Data的对象

其实这种和之前提到的index-handle有点像

这里介绍Bulk Data

作者也没有好的术语来概括他们,大概就是一组数据

  • 游戏中所有的子弹
  • 游戏中所有的树
  • 游戏中所有的硬币

更抽象一点

  • 所有游戏中的项目
  • 所有游戏中的网格物品
  • 所有游戏中的声音/音效

并且每种类型都有很多属性来判断,对于音效

  • 所有能被播放的声音资源
  • 当前播放的声音
  • 所有能加到音轨上的音效(淡入淡出,摩擦声等等)

对于bulk data资源,我们假定

  • 顺序不重要,当成set
  • 每种类型都是POD类型,可以memcpy

可能某些场景顺序也有要求,比如需要渲染的对象,需要从头到尾渲染一遍

作者推荐把需要顺序的捞出来主动排序一下,而不是用个能排序的容器来保存,基数排序O(n) 也能接受(本来数据规模也不大)

至于POD类型,这里假定所有对象都是固定大小的POD,至于有的可能是链表结构这个放到后面数组的数组在讨论

这里还以声音资源来举例

typedef struct {
    resource_t * resource; // 资源 引用
    uint64_t bytes;        // 资源大小
    uint64_t format;       // 资源的格式判断位
} sound_resource_t;

typedef struct {
    sound_resource_t *resource; // 正在播放的资源 引用
    uint64_t samples_played;    // 播放了的个数
    float volume;               // 音量
} playing_sound_t;

typedef struct {
    playing_sound_t *sound;     // 渐入的资源 引用
    float fade_from;            // 音量
    float fade_to;              // 音量
    double fade_from_ts;        // 什么时候开始渐入
    double fade_to_ts;          // 什么时候结束渐入
} playing_fade_t;

我们对于保存bulk data有这么几个要求

  • 增删对象要快
  • 对象的存储布局要cache-friendly,能更快的遍历
  • 支持引用
  • allocator-friendly 对于分配器友好?不如说分配器要牛逼一点

最简单的实现bulk data的方法就是用一个静态数组或者一个vector

#define MAX_PLAYING_SOUNDS 1024
uint32_t num_playing_sounds;
playing_sound_t playing_sounds[MAX_PLAYING_SOUNDS];

// C++ vector
std::vector<playing_sound_t> playing_sounds;

如果你知道对象的个数用静态数组非常见到粗暴,如果不确定,可能浪费或者不够用

使用std::vector要注意一些问题

  • DEBUG模式下 VS的std::vector非常慢,要注意设置 _ITERATOR_DEBUG_LEVEL=0
  • std::vector用构造析构创建删除对象,在某些场景要比memcpy慢(应该指的搬迁)
  • std::vector更难自省??要比自己实现一个更难观测(stretchy弹性)

另外两者都不支持对象引用???

删除策略

如果a[i]被删,几种处理手段

  • 搬迁后面的节点,覆盖空的slot 太浪费时间,除非你要保证顺序(尤其是erase)

  • 直接搬迁最后面的,swap-and-pop

    std::swap(a[i], a[a.size() - 1]);
    a.pop_back();
    

    直接从后面分配,无脑

    • 更紧凑遍历更快
    • index会变,就会类似hashtable重整,属性全丢了
  • 留着这个洞,后面直接分配这个 freelist 需要在用slot前检查

    • index信息没变
    • 遍历有洞会慢

作者建议各取所需,除非对遍历有强需求,否则第三种很简单,idindex信息也都在,使用更方便

可能最终维护框架长这个样子

// The objects that we want to store:
typedef struct {...} object_t;

// An item in the free list points to the next one.
typedef struct {
    uint32_t next_free;
} freelist_item_t;

// Each item holds either the object data or the free list pointer.
typedef union {
    object_t;
    freelist_item_t;
} item_t;

typedef struct {
    std::vector<item_t> items;
} bulk_data_t;

void delete_item(bulk_data_t *bd, uint32_t i) {
    // Add to the freelist, which is stored in slot 0.
    bd->items[i].next = bd->items[0].next;
    bd->items[0].next = i;
}

uint32_t allocate_slot(bulk_data_t *bd) {
    const uint32_t slot = bd->items[0].next;
    bd->items[0].next = bd->items[slot].next;
    // If the freelist is empty, slot will be 0, because the header
    // item will point to itself.
    if (slot) return slot;
    bd->items.resize(bd->items.size() + 1);
    return bd->items.size() - 1;
}

weak pointers

直接用索引信息就可以了

加上版本信息就可以了

typedef struct {
    uint32_t id;         // index
    uint32_t generation; // 引用计数类似物,版本号?
} weak_pointer_t;


typedef struct {
    uint32_t generation;
    union {
        object_t;
        freelist_item_t;
    };
} item_t;

分配策略

std::vector本身有内存放大问题,如果满了就会扩容搬迁,这导致了一些问题

  • 扩容造成的复制开销很大,可能造成延迟,这也是GC在游戏中会造成延迟的问题
  • 内存浪费

解决办法

结构体数组还是数组结构体?

数组结构体

  • 代码复杂
  • 分配器压力,分配被迫分散到不同的数组,而不是分配一个大的
  • 字段分散,得有index信息分别访问
  • 增加了计算量
  • cache不友好 但是紧凑起来可以加速simd
  • 删除不友好

最终结论 bulk data保存最终方案

There are advantages and drawbacks to everything, but my default recommendation for storing bulk data for a new system would be:

An array of structures, with “holes” and permanent pointers, either allocated as one single large VM reservation (if possible) or as an array of fixed size blocks (of 16 K or whatever is a good fit for your data).

And for the cases where you need really fast number crunching over the data:

A structure of arrays of tightly packed objects, grouped 8 at a time for SIMD processing and allocated as one single large VM reservation, or as an array of fixed-size blocks.

结构体数组 方便管理

数组结构体,可以用simd加速,但是不好管理

ref

  • 我是看lobste上的推荐看的这篇文章https://ourmachinery.com/post/data-structures-part-1-bulk-data/
  • 后面的系列
    • https://ourmachinery.com/post/data-structures-part-2-indices/
    • https://ourmachinery.com/post/data-structures-part-3-arrays-of-arrays/
  • 作者自己实现类似vector的容器 值得一看https://ourmachinery.com/post/minimalist-container-library-in-c-part-1/

看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

(译)Handles are the better pointers

作者的一个经验,把指针转换成index-handles,也就是如何写不涉及分配和指针的程序

背景

  • 0.5到1百万行代码,智能指针管理内存
  • 万到十万以上的小对象,堆分配,智能指针管理

这种管理方式基本不会有segfault,有问题的代码也都能抓到问题根源

但是存在问题

  • dog-slow?遍地都是cache-miss
  • 内存碎片问题
  • fake memory leaks 内存指针占用的内存没有及时回收,看上去像是内存泄漏了,而这种问题通过内存检测工具也检查不出来

作者给的方法有一定借鉴意义(甚至对于那些GC语言,因为会面临同样的场景,大量小对象)

但是可能要要求创建销毁对象尽可能的集中,某些场景可能不可行,也就是面向数据

这里的使用场景是游戏场景,有大量小对象频繁创建删除

简要概括

  • 所有的内存管理集中成一个模块,用这个模块来管理分配
  • 把小对象分类,每个小对象有自己的数组指针-索引来管理
  • 创建对象,返回index-handle就可以了
  • 需要的话,就把handle强转指针来用,并且任何地方都不要保存指针

感觉这是个很常见的CRTP分配器套路,尽可能的复用内存,对于代理型应用,都会这么设计,作者是游戏型应用,也是有大量小对象大量的创建删除的。一个好的分配器能降低系统碎片且让cpu利用率高,不轻易cache miss –这些都影响延迟

下面作者说了针对他们这个场景的几点优势

  • 管理高效
  • cache friendly
    • 内存数据尽可能的热
  • 不会有很多的内存碎片

全面用这个index-handle替换指针的优势

  • 避免直接访问内存,安全
  • 只有分配器组件知道这个内存分配的细节和指针信息,别人只能通过index拿到 (其实index也可以隐藏起来)
  • 内存增长更安全,会尽可能复用,不会激增

但是这要求应用本身不能乱用指针,因为引用的对象随时会变

这个东西和普通的CRTP 内存池还不太一样。代码我还没有读。有些作者的设计点我没有领会完整。后面有时间会看看

ref

  • https://floooh.github.io/2018/06/17/handles-vs-pointers.html
  • 作者的封装库 https://github.com/floooh/sokol#sokol_gfxh
  • 背景文档 https://floooh.github.io/2018/06/02/one-year-of-c.html 也值得一看
  • 我是看lobste上的推荐看的这篇文章,还有https://ourmachinery.com/post/data-structures-part-1-bulk-data/ 后面也要看看

看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

Seastar资料整理以及介绍

  • https://forrestsu.github.io/posts/archi-seastar/seastar-future-promise/ future promise原理

future promise穿成链表,包装task回调

  • https://zhuanlan.zhihu.com/p/113119124

介绍了主要特点 核间通信,以及一些优化上的优点

用到的无锁队列

使用的应用

cpv-framework: A web framework written in c++ based on seastar framework

redpanda: A Kafka replacement for mission critical systems

Scylla: A fast and reliable NoSQL data store compatible with Cassandra and DynamoDB

smf: rpc框架,用的fbs

seastar侵染性太强,沾上就得用


看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

folly资料整理以及介绍

  • IOThreadPool :每个线程中包含一个 EventLoop ,即一个 epoll 的处理器。添加任务时,添加到通知队列, epoll 循环可以收到通知,处理任务。额外还可以添加IO事件回调。
  • CPUThreadPool :和 IOThreadPool 相比,它简单一些,添加任务时,添加到阻塞队列,以信号的形式通知线程,空闲线程执行任务。

  • ConcurrentHashMap 基于hazard pointer https://zhuanlan.zhihu.com/p/104308755
  • 为啥有concurrenthashmap还要又个atomichashmap? 实现原理差不多 https://github.com/facebook/folly/blob/master/folly/concurrency/ConcurrentHashMap.h

  • iobuf 参考的tcp协议栈里的mbuf https://github.com/facebook/folly/blob/master/folly/io/IOBuf.h
    • 这个东西我看redpanda也用了。要省network 浪费, iobuf避免不了

wangle介绍

https://my.oschina.net/fileoptions/blog/881909


看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

七月待读 need review

我发现越攒越多了这东西

https://medium.com/swlh/building-rest-api-backed-by-redis-ae8ff4818460

https://github.com/mhewedy-playground/RecipeAPI/blob/master/main.go

https://tech.marksblogg.com/omnisci-macos-macbookpro-mbp.html

https://medium.com/google-cloud/spanners-sql-story-79bda8bb632d

https://zhuanlan.zhihu.com/p/141891855

https://fauna.com/blog/demystifying-database-systems-part-4-isolation-levels-vs-consistency-levels

https://pingcap.com/blog/how-tidb-htap-makes-truly-hybrid-workloads-possible/

https://blog.yugabyte.com/why-distributed-sql-beats-polyglot-persistence-for-building-microservices/?utm_source=db_weekly&utm_medium=sponsored_link&utm_campaign=july172020

https://vladmihalcea.com/sql-left-join/

https://tiledb.com/blog/tiledb-closes-15m-series-a-for-industry-s-first-universal-data-engine-2020-07-14

https://ketanbhatt.com/db-concurrency-defects/

leveldb pdf 已经读过了。记录一下

https://yuerblog.cc/wp-content/uploads/leveldb%E5%AE%9E%E7%8E%B0%E8%A7%A3%E6%9E%90.pdf

C++ 中把 lambda 优雅地转化为函数指针 · Terark/terarkdb Wiki

https://github.com/Terark/terarkdb/wiki/C%EF%BC%8B%EF%BC%8B-%E4%B8%AD%E6%8A%8A-lambda-%E4%BC%98%E9%9B%85%E5%9C%B0%E8%BD%AC%E5%8C%96%E4%B8%BA%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88

看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

python笔记以及遇到的坑

## pyyaml

  • pyyaml会有特殊的字符串转换,比如把yes转换成true,所以必须要把yes用引号括起来,或者定制yaml的构造函数,定制resolver,见参考链接的做法。

    • 修改意见:主动引号括起来。或者换名字。定制resolver杀鸡牛刀
  • pyyaml对于一些key会加引号,当初由于上层应用不是标准yaml库解析,不能解析引号,所以采用了ruamel.yaml这个库,设置指定的dumper可以让字段不带引号,但是这个库的实现有很大问题,建议别用,而且你能搜到该库作者的很多SO网站回答推广,真有你的啊

    • 不要用,出来混早晚要还的。千万别取巧
  • eventlet 0.25版本,会依赖dnspython库,这个库不能用2.0版本,接口不兼容

    • 解决方法 固定dnspython在1.16或者升级eventlet到0.25.2以上

xml

之前以为xml库难用,其实是我不理解xml

hdfs的配置文件格式xml ,我需要解析一个配置项,改值

  <property>
      <name>dfs.client.failover.max.attempts</name>
      <value>10</value>
      <description>
      if multiply namenodes are configured, it is the max retry times when the dfs client try to issue a RPC call. default is 75.
      </description>
  </property>
  <property>
      <name>dfs.ratelimiter.enabled</name>
      <value>false</value>
      <description>
      ratelimiter enabled or not.
      </description>
  </property>
  
  <property>
      <name>limiter.bandwidth</name>
      <value>62914560</value>
      <description>
      the limited highest bandwidth.
      </description>
  </property>
  
  <property>
      <name>limiter.iops</name>
      <value>60000</value>
      <description>
      the limited highest iops.
      </description>
  </property>
  

主要是限流的改动,全去掉

依据我之前使用json yaml的经验,都是kv型的,怎么也改不了value字段的值

部门高手写了这么段代码,真暴力

xml是树型的,都是子节点,也就是说上面匹配到了text,那下一个肯定是value字段。。。

我之前的考虑就是奔着value字段改的。笨比了。

  import sys
  import xml.etree.ElementTree as ET
  
  tree = ET.parse('hdfs-client.xml')
  root = tree.getroot()
  
  found = False
  for child in root.iter():
      if child.text == 'dfs.client.failover.max.attempts':
          found = True
          continue
      if found:
          child.text = "111"
          found = False
          break
  
  
  tree.write("hdfs-client.xml")
  
  • xml真是太让我头疼了

进程连接不上

  • 网络问题
  • 进程卡死

不要很自信的说网络问题。自己模块写的有问题进程卡死也是有可能的

Python核心语法

  • 共享引用 在原处更改

is == 区别

  • 小的整数和字符串缓存复用了 sys.getrefcount(1)

  • 函数
    • def 生成函数对象并复制给函数名 lambda直接返回函数
    • yield 返回一个结果对象,保存上下文
    • global 模块级变量 nonlocal 类似global,用于闭包
    • 闭包,有点像c++里的函数对象 python中类实现__call__(self)也行,书上说这种写法过于重型
  • 参数
    • 传参是引用还是传值 In-Place Change
    • 避免 主动传值[:],传tuple
    • 任一参数*pargs kargs pargs是元组 **kargs是字典,用来解包 引申-》Keyword-Only 用来指定元组和字典。单独使用做占位参数,则需要调用方保证指定所有入参(a=8这种)注意必须是最后一个参数
  • 再谈函数
    • map(functor, iterable-obj) std::for_each filter std::remove functools.reduce std::acc,
    • 迭代,next()语法糖
      • yield,生成器,for,range这些都是生成器函数
      • 生成器表达式
      • 可迭代对象
  • 模块
    • import from from 危害,模块冲突
    • import 决定将__name__ = ‘main‘解释成模块还是函数
    • 运算符重载
    • __str__
    • __repr__
    • 继承,真正意义上的接口。不会调用基类构造函数
from abc import abstractmethod
@abstractmethod

__getitem__ 买一送一

_iter__

__next__

__del__

__getaddr__ 属性钩子

__slot__

  • 装饰器与元类
    • 类做装饰器
    • 函数装饰类 元类
class tracer:
  def __init__(self, func):
    self.func = func;
  def __call__(self,*args):
    self.func(*args)
  • 异常
    • try except finally
    • raise assert
    • with as
      • with 环境管理协议 enter exit
      • 主动释放with内资源。包括多线程的锁条件变量之类dir

ref

  • https://stackoverflow.com/questions/34282703/yes-or-no-dumping-as-yes-or-no-in-pyyaml
  • https://github.com/eventlet/eventlet/issues/619

看到这里或许你有建议或者疑问或者指出我的错误,请留言评论或者邮件mailto:wanghenshui@qq.com, 多谢!

觉得写的不错可以点开扫码赞助几毛 微信转账
Read More

^