Python

解释性语言

解释型语言没有严格编译汇编过程,由解释器将代码块按需要变运行边翻译给机器执行。因此解释型语言一度存在运行效率底,重复解释的问题。但是通过对解释器的优化!可以提高解释型语言的运行效率。

Python 与大多数解释型语言一样,确实是将源代码编译为一组虚拟机指令,并且 Python 解释器是针对相应的虚拟机实现的。这种中间格式被称为 “字节码”。

Python 字节码

Python 以 .pyc 结尾的 “ 字节码(bytecode)” 文件(二进制文件),一般位于__pycache__ 的子目录中,可以避免每次运行 Python 时去重新解析源代码。

1
python -m py_compile file.py  # 生成单个pyc文件

python程序运行中的 Python 字节码 是什么时候产生的,我修改py文件,其对应的字节码文件会在何时更新

gpt3.5: Python字节码是由Python解释器在执行Python源代码之前生成的中间表示形式。它是将源代码翻译为机器可以理解的指令序列的一种形式。字节码文件的扩展名通常为.pyc(Python 3)或.pyo(Python 2)。

当你运行Python程序时,如果存在对应的字节码文件(.pyc或.pyo),解释器会首先检查字节码文件的时间戳。如果字节码文件的时间戳比源代码文件的时间戳更晚,且没有进行代码的修改,解释器会直接加载和执行字节码,跳过源代码的编译过程。

当你修改了Python源代码文件时,字节码文件不会立即更新。Python解释器会在下一次运行程序时,检测到源代码文件的修改,并重新编译生成新的字节码文件。这样,更新后的字节码文件将反映源代码的更改,并用于后续的执行。

访问和理解 Python 字节码

经过编译的python文件可以提高程序的运行速度,一定程度上也对源代码起到了保护作用。然而如果我们只有编译过的python字节码文件,就给我们审查源码造成了一定的困难,这就引出了python字节码反编译的需求。

如果你想玩转字节码,那么,Python 标准库中的 dis 模块将对你有非常大的帮助;dis 模块为 Python 字节码提供了一个 “反汇编”,它可以让你更容易地得到一个人类可读的版本,以及查找各种字节码指令。

知道如何去访问和阅读 Python 字节码将让你很容易回答为什么某些结构比其它结构运行的更快这样的问题(比如,为什么 {} 比 dict() 快)(尝试对比一下: dis.dis(“{}”) 与 dis.dis(“dict()”) 就会明白)。

pyo优化文件

pyo文件是源代码文件经过优化编译后生成的文件,是pyc文件的优化版本。编译时需要使用-O和-OO选项来生成pyo文件。在Python3.5之后,不再使用.pyo文件名,而是生成文件名类似“test.opt-n.pyc的文件。

1
python -O -m py_compile test.py

Python 虚拟机

CPython 使用一个基于栈的虚拟机。(你可以 “推入” 一个东西到栈 “顶”,或者,从栈 “顶” 上 “弹出” 一个东西来)。

CPython 使用三种类型的栈:

  1. 调用栈(call stack)。这是运行 Python 程序的主要结构。它为每个当前活动的函数调用使用了一个东西 —— “ 帧(frame)”
  2. 在每个帧中,有一个 **计算栈(evaluation stack)**(也称为 数据栈(data stack))。这个栈就是 Python 函数运行的地方,运行的 Python 代码大多数是由推入到这个栈中的东西组成的,操作它们,然后在返回后销毁它们。
  3. 在每个帧中,还有一个**块栈(block stack)**。它被 Python 用于去跟踪某些类型的控制结构:循环、try / except 块、以及 with 块,全部推入到块栈中,当你退出这些控制结构时,块栈被销毁。

C vs Python

运行流程区别

python的传统运行执行模式:录入的源代码转换为字节码,之后字节码在python虚拟机中运行。代码自动被编译,之后再解释成机器码在CPU中执行。

c编译器直接把c源代码编译成机器码。过程比python执行过程少了字节码生成和虚拟机执行字节码过程。所以自然比python快。

深、浅拷贝

Python append() 与深拷贝、浅拷贝

python赋值只是引用,别名

1
2
3
4
5
6
7
8
list.append('Google')   ## 使用 append() 添加元素
alist.append( num ) # 浅拷贝 ,之后修改num 会影响alist内的值

import copy
alist.append( copy.deepcopy( num ) ) # 深拷贝

# delete
del list[2]

for循环迭代的元素 也是 引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
original_list = [1, 2, 3]

for item in original_list:
item *= 2 # 每个元素是不可变的

print(original_list)

original_list = [[1,2,3], [2], [3]]

for item in original_list:
item.append("xxx") # 每个元素是可变的

print(original_list)

# [1, 2, 3]
# [[1, 2, 3, 'xxx'], [2, 'xxx'], [3, 'xxx']]

[函数传参是引用,但是能通过切片来得到类似指针](https

参数的传递
函数声明时的形参,使用时,等同于函数体内的局部变量。由于Python中一切皆为对象。因此,参数传递时直接传递对象的地址,但具体使用分两种类型:

  1. 传递不可变对象的引用(起到其他语言值传递的效果) 数字,字符串,元组,function等
  2. 传递可变对象的引用(起到其他语言引用传递的效果) 字典,列表,集合,自定义的对象等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def fun0(a):
a = [0,0] # a在修改后,指向的地址发生改变,相当于新建了一个值为[0,0]

def fun(a):
a[0] = [1,2]

def fun2(a):
a[:] = [10,20]

b = [3,4]
fun0(b)
print(b)
fun(b)
print(b)
fun2(b)
print(b)

# [3, 4]
# [[1, 2], 4]
# [10, 20]

return 返回值, 可变对象的也是引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def fun1(l):
l.append("0")
return l

def fun2(l):
return l

if __name__=="__main__":
l = [1,2,3,4,5]

rel2 = fun2(l)
print(rel2)
rel1 = fun1(l)
print(rel1)
print(rel2)

# [1, 2, 3, 4, 5]
# [1, 2, 3, 4, 5, '0']
# [1, 2, 3, 4, 5, '0']

逻辑

setup

setup安装包的过程,请看pip package一文。

__init__.py

__init__.py文件在 Python 包结构中扮演着重要角色,但是否必须存在取决于你使用的 Python 版本和具体的使用场景。

  1. Python 2

    • __init__.py 是必需的。它标志着一个目录是一个 Python 包,允许该目录中的模块被导入。
  2. Python 3

    • 隐式命名空间包:从 Python 3.3 开始,__init__.py 不再是必需的。Python 3 支持隐式命名空间包(Implicit Namespace Packages),这意味着即使没有 __init__.py 文件,目录也可以被视为包并进行导入。
    • 显式初始化逻辑:尽管 __init__.py 不再是必需的,但它仍然是推荐的做法,特别是在需要执行包级别的初始化逻辑时。

__init__.py 的作用

即使在 Python 3 中,__init__.py 文件仍然有许多用途:

  1. 包初始化
  • 可以包含包级别的初始化代码,例如设置全局变量、注册插件等。
  1. 控制导出内容
  • 使用 __all__ 列表来控制哪些符号可以被 from package import * 导入。
  1. 定义包级别的函数和类
  • 可以直接在 __init__.py 中定义函数和类,这些函数和类可以直接通过包名访问。
  1. 子模块导入
  • 可以在 __init__.py 中导入子模块,使得用户可以通过包名直接访问子模块的内容。

__init__.py 文件的示例

假设你有以下文件结构:

  • mindspeed_mm/
  • __init__.py
  • models/
    • __init__.py
    • internvl_model.py

mindspeed_mm/__init__.py

1
2
3
# mindspeed_mm/__init__.py

print("Initializing mindspeed_mm package.")

mindspeed_mm/models/__init__.py

1
2
3
4
5
# mindspeed_mm/models/__init__.py

from .internvl_model import InternVLModel

print("Initializing models package.")

mindspeed_mm/models/internvl_model.py

1
2
3
4
5
# mindspeed_mm/models/internvl_model.py

class InternVLModel:
def __init__(self):
print("InternVLModel instance created.")

pretrain_internvl.py

1
2
3
4
5
# pretrain_internvl.py

from mindspeed_mm.models import InternVLModel

model = InternVLModel()

在这个例子中:

  • mindspeed_mm/__init__.pymindspeed_mm/models/__init__.py 中的代码会在导入时执行。
  • mindspeed_mm/models/__init__.py 中的 from .internvl_model import InternVLModel 使得你可以通过 from mindspeed_mm.models import InternVLModel 直接导入 InternVLModel 类。

import

命名空间(namespace)可以基本理解成每个文件是一个,通过import来使用

触发 __init__.py

  • 当你导入一个包时,Python 会执行该包目录下的 __init__.py 文件。如果没有这个文件,Python 会认为这个目录不是一个包,因此 import 语句会失败。
  • __init__.py 负责初始化这个包,可以定义一些包级别的变量、函数或导入包的其他子模块。

行为

  • 每次导入包时,__init__.py 文件只会在第一次导入时被执行一次。如果模块已经被导入到当前的命名空间,再次 import 不会重新执行 __init__.py,除非你强制重新加载(比如用 importlib.reload())。
  • import 的执行会触发模块的初始化,类似于 C++ 中构造函数的概念,但不是在对象级别,而是在模块级别。
1
2
3
4
5
# example/__init__.py
print("Initializing the package")

def hello():
print("Hello from the package")
1
2
3
4
import example
# 输出 "Initializing the package"
example.hello()
# 输出 "Hello from the package"

import xxx missing

python 会优先导入本地目录的同名文件夹先于安装包。

优先级

  1. 当前工作目录
  2. PYTHONPATH 查看 env|grep PYTHONPATH
  3. site-packages/*.pth
  4. site-packages

绝对导入与相对导入

场景 导入方式 示例
导入标准库/第三方库 绝对导入 import numpy as np
导入项目根目录下的模块 绝对导入 from my_project import config
包内模块互相引用 相对导入 from . import helper
直接运行脚本 绝对导入 python script.py
作为包的一部分运行 相对导入或绝对导入 python -m package.module

在 Python 中,import 语句是否使用点号(.)取决于你使用的是 相对导入 还是 绝对导入,以及代码所处的上下文环境。以下是详细的解释:


绝对导入(不带点号)

语法import package.modulefrom package import module
适用场景

  • 从项目的根目录或已安装的包开始导入。
  • 模块位于 sys.path 中的路径下(例如:标准库、第三方包、已添加到环境变量中的自定义模块)。

示例

1
2
3
4
5
6
# 导入标准库(绝对路径)
import os
from datetime import datetime

# 导入项目根目录下的模块
from my_project.utils import helper

关键点

  • 不需要点号,路径从顶级包开始。
  • 适用于大部分情况,尤其是模块作为独立脚本运行时。

相对导入(带点号)

语法from . import modulefrom ..subpackage import module
适用场景

  • 包(Package)内部的模块之间互相引用。
  • 使用点号表示当前模块的位置(. 表示当前目录,.. 表示父目录)。

示例

1
2
3
4
5
6
7
8
my_package/
├── __init__.py
├── utils/
│ ├── __init__.py
│ └── helper.py
└── core/
├── __init__.py
└── main.py

main.py 中导入同级的 utils/helper.py

1
2
# 相对导入(当前包内)
from ..utils import helper

关键点

  • 必须存在于包中(目录必须有 __init__.py)。
  • 不能作为脚本直接运行(如 python main.py),否则会报错:
    1
    ImportError: attempted relative import with no known parent package
  • 适用于包内部的模块间引用,避免硬编码绝对路径。

入口

  • 在Python中,if __name__ == "__main__"这种写法通常出现在模块中,它的作用是控制模块的执行流程。
  • 当一个模块被导入时,Python解释器会自动将这个模块的__name__属性设置为模块名称。但是如果模块是被直接运行的,则__name__属性会被设置为字符串__main__。
  • 所以if name == “main”可以用来区分模块是被导入运行还是被直接运行:
    • 如果模块是被导入的,if语句不会执行。因为模块的__name__不等于__main__。
    • 如果模块是被直接运行的,if语句会执行。因为模块的__name__等于__main__。

from xxx.yyy import zzz行为

清理与释放

程序结束时的清理行为(类似析构函数的操作)

在 Python 中,并没有像 C++ 那样显式的析构函数。模块或对象的清理一般通过以下方式实现:

  • 对象的析构:当一个 Python 对象的引用计数降为零时,Python 会自动调用该对象的 __del__ 方法进行资源清理。这个机制类似于 C++ 的析构函数,但触发时机取决于 Python 的垃圾回收机制。
1
2
3
4
5
6
7
8
9
class MyClass:
def __init__(self):
print("Object created")

def __del__(self):
print("Object destroyed")

obj = MyClass()
# 程序结束时,或者当 obj 的引用计数降为 0 时,触发 __del__()
  • 模块的清理:当程序结束时,Python 会尝试清理已加载的模块。这个过程会调用模块内一些特殊的钩子函数来进行必要的清理工作。虽然 Python 没有直接为模块提供析构函数,但是你可以使用 atexit 模块来注册一个函数,确保在程序结束时执行。

示例:使用 atexit 实现模块级别的清理操作

1
2
3
4
5
6
7
8
9
import atexit

def cleanup():
print("Cleaning up resources before program exit")

# 注册一个清理函数,在程序结束时自动调用
atexit.register(cleanup)

print("Program is running")

输出

1
2
Program is running
Cleaning up resources before program exit
  • atexit 模块允许你注册多个函数,它们会在解释器关闭之前按注册顺序依次执行。
  • 这种机制相当于 C++ 中的全局或静态对象析构函数的功能,确保在程序结束时执行一些清理工作。

模块的生命周期总结

  • 初始化:当模块被导入时,Python 会执行模块的顶层代码,包括 __init__.py 文件。这相当于模块的 “构造” 过程。
  • 对象的析构:在 Python 中,通过垃圾回收机制和 __del__ 方法来管理对象的生命周期。通常情况下,当对象不再被引用时,会自动触发清理。
  • 程序结束时的清理:Python 提供了 atexit 模块来执行程序结束时的资源清理操作。你可以在模块中注册一些函数,确保在程序退出时执行清理任务。

与 C++ 的比较

  • Python 的模块和包机制类似于 C++ 中的构造函数,但它的作用范围是模块级别的,而不是对象级别的。
  • Python 通过垃圾回收和 __del__ 方法来处理对象的清理,而不是像 C++ 中的显式析构函数。
  • Python 提供了 atexit 模块来实现程序级别的清理操作,这类似于 C++ 中全局/静态对象的析构行为,但更加灵活。

语法

常见运算符

以下是 Python 和 C++ 中一些常见的运算符及其差异:

基本算术运算符

运算符 描述 Python 示例 C++ 示例
// 整数除法 a // b a / b (整数除法)
% 取模 a % b a % b
** a ** b pow(a, b)

位运算符

运算符 描述 Python 示例 C++ 示例
& 按位与 a & b a & b
` ` 按位或 `a
^ 按位异或 a ^ b a ^ b
~ 按位取反 ~a ~a
<< 左移 a << b a << b
>> 右移 a >> b a >> b

赋值运算符

运算符 描述 Python 示例 C++ 示例
//= 整数除法赋值 a //= b a /= b (整数除法)
%= 取模赋值 a %= b a %= b
**= 幂赋值 a **= b a = pow(a, b)
&= 按位与赋值 a &= b a &= b
` =` 按位或赋值 `a
^= 按位异或赋值 a ^= b a ^= b
<<= 左移赋值 a <<= b a <<= b
>>= 右移赋值 a >>= b a >>= b

逻辑运算符

运算符 描述 Python 示例 C++ 示例
and 逻辑与 a and b a && b
or 逻辑或 a or b `a
not 逻辑非 not a !a

成员运算符

运算符 描述 Python 示例 C++ 示例
in 成员 a in b 无直接等价
not in 非成员 a not in b 无直接等价

身份运算符

运算符 描述 Python 示例 C++ 示例
is 身份相同 a is b 无直接等价
is not 身份不同 a is not b 无直接等价

装饰器 decorator

@能在最小改变函数的情况下,包装新的功能。^1

1
2
3
4
5
6
7
8
9
10
11
12
def use_logging(func):

def wrapper():
logging.warn("%s is running" % func.__name__)
return func()
return wrapper

@use_logging
def foo():
print("i am foo")

foo()
  • 问题:但是上面的代码会导致函数名被修改,所以不能再使用 foo.__name__变成了use_logging
  • 解决办法:加上@wraps(func)
1
2
3
4
5
6
7
def use_logging(func):

@wraps(func)
def wrapper():
logging.warn("%s is running" % func.__name__)
return func()
return wrapper

下划线

单下划线、双下划线、头尾双下划线说明:

  • __foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 init() 之类的。
  • _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
  • __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。

函数传参

解包

  • 解包是指将一个容器(如列表、元组或字典)的内容拆分并分配给多个变量或作为参数传递给函数。
  • Python 提供了简洁的语法来实现这一点,使用 *** 分别解包可迭代对象和字典。

*args 和 **kwargs

在 Python 中,*args**kwargs 是非常强大的工具,用于处理可变数量的参数。它们使得函数可以接收任意数量的位置参数和关键字参数,并将这些参数传递给其他函数或方法。让我们详细解释一下你提供的代码片段:

1
2
3
4
5
6
7
8
9
10
def __call__(self, *input, **kwargs):
result = self.forward(*input, **kwargs)
# 假设 kwargs 包含你传递的参数字典
key1_value = kwargs.get('key1', None) # 使用 get 方法安全地获取 key1 的值

if key1_value is not None:
# 在这里使用 key1 的值
print(f"The value of key1 is: {key1_value}")
else:
print("key1 is not provided in the arguments.")

为什么使用 *** 可以传递参数

  1. 收集参数
  • *input:收集所有未命名的位置参数(非关键字参数),并将它们打包成一个元组。
  • **kwargs:收集所有未明确列出的关键字参数,并将它们打包成一个字典。
  1. 解包参数
  • 在调用 self.forward 时,*input 将之前收集的位置参数解包为单独的参数传递给 forward 方法。
  • 同样,**kwargs 将之前收集的关键字参数解包为单独的关键字参数传递给 forward 方法。

具体工作原理

当你调用 __call__ 方法时,你可以传递任意数量的位置参数和关键字参数。例如:

1
2
3
4
5
6
obj = SomeClass()
obj(1, 2, 3, key1='value1', key2='value2')

params = {'key1': value1, 'key2': value2} # 示例参数字典
# 使用 ** 解包字典作为关键字参数传递给 __call__ 方法
obj(**params)

在这个例子中:

  • 1, 2, 3 被收集到 *input 中,形成元组 (1, 2, 3)
  • key1='value1', key2='value2' 被收集到 **kwargs 中,形成字典 {'key1': 'value1', 'key2': 'value2'}

解包可迭代对象

函数定义中的 *

  • 位置参数收集

  • 在函数定义中,*args 用于收集所有未命名的位置参数(非关键字参数),并将它们打包成一个元组。

  • 强制关键字参数

  • 如果在参数列表中使用了单独的 *,那么 * 后面的所有参数必须以关键字形式传递。

示例

1
2
3
4
5
def example_function(a, b, *args):
print(f"a: {a}, b: {b}")
print("Additional positional arguments:", args)

example_function(1, 2, 3, 4, 5)

输出:

1
2
a: 1, b: 2
Additional positional arguments: (3, 4, 5)

强制关键字参数

1
2
3
4
5
6
7
8
def another_function(a, b, *, x, y):
print(f"a: {a}, b: {b}, x: {x}, y: {y}")

# 下面的调用会报错,因为 x 和 y 必须是关键字参数
# another_function(1, 2, 3, 4)

# 正确的调用方式
another_function(1, 2, x=3, y=4)

输出:

1
a: 1, b: 2, x: 3, y: 4

函数调用中的 *

1
2
3
4
5
6
def sum_three_numbers(x, y, z):
return x + y + z

numbers = [1, 2, 3]
result = sum_three_numbers(*numbers)
print(result) # 输出:6

解包字典

  • 在函数定义中,** 用于将传入的关键字参数打包成一个字典;
  • 而在函数调用中,** 则用于将字典解包为关键字参数。

函数定义中的 **kwargs

当你在函数定义中使用 **kwargs 时,所有未明确列出的关键字参数都会被收集到一个名为 kwargs 的字典中。

1
2
3
4
5
def example_function(a, b, **kwargs):
print(f"a: {a}, b: {b}")
print("Additional arguments:", kwargs)

example_function(1, 2, x=3, y=4)

输出:

1
2
a: 1, b: 2
Additional arguments: {'x': 3, 'y': 4}

函数调用中的 ** 解包字典

当你在函数调用中使用 ** 时,它会将字典中的键值对解包为关键字参数传递给函数。这意味着字典的键会成为参数名,对应的值会成为参数值。

1
2
3
4
5
def another_function(a, b, x, y):
print(f"a: {a}, b: {b}, x: {x}, y: {y}")

args_dict = {'x': 3, 'y': 4}
another_function(1, 2, **args_dict)

输出:

1
a: 1, b: 2, x: 3, y: 4

在这个例子中,args_dict 是一个字典,包含键 xy 及其对应的值。通过 **args_dict,这些键值对被解包为关键字参数传递给 another_function

DEBUG

breakpoint()

python 能很轻易的

段错误

  1. 开启 Python 的调试模式
    通过设置环境变量启用 Python 的调试信息,这有助于捕获异常和详细的堆栈信息。

    1
    export PYTHONMALLOC=debug
  2. 使用 faulthandler 模块
    Python 提供了一个 faulthandler 模块,可以用来捕获段错误并打印堆栈信息。你可以在程序的开头添加以下代码来启用它:

    1
    2
    import faulthandler
    faulthandler.enable()

    这将会在段错误发生时输出堆栈跟踪。

  3. 查看 Python 调试输出
    启动 Python 程序时,通过 faulthandler 打印堆栈信息,或通过 GDB 调试 Python 解释器。如果 Python 解释器发生崩溃,faulthandler 会帮助你定位错误。

卡住任务

pstack remote pid , 虽然官方没说,但是可以pip install pystack 或者 pip install pstack安装

py-spy dump –pid 831568

INFO

torch.profiler

如果程序能正常运行,优先推荐profiling理解程序逻辑。

打印当前堆栈

traceback.print_stack()

VizTracer时间性能分析

1
2
3
4
5
6
7
8
9
10
from viztracer import VizTracer

tracer = VizTracer(max_stack_depth=2) # 限制记录的调用栈深度为2,常用为 50和120
tracer.start()

# 你的代码
your_function()

tracer.stop()
tracer.save("result.json")

或者

1
2
3
from viztracer import VizTracer
with VizTracer(output_file="xxx.json", tracer_entries=20000000) as tracer:
xxx

VizTracer专注于时间占比,py-spy专注于调用逻辑

VizTracer 知道“我现在进了一个新函数”,但它并不会、也无法在这一刻完整扫描“是谁调用了我”。

logevent 弥补了一点能力。

1
2
3
4
from viztracer import get_tracer

with get_tracer().log_event("my event name"):
# some code running here

可以尝试 火焰图 py-spy record -o profile.svg --rate 100 your_script

mstx 逻辑监控工具

  • torch_npu.mstx.mark range_start
  • 参考putils

mstt NPU vs GPU 性能对比工具

能实现到excel的输出,参考文档

msprobe 精度对比工具

msprobe是MindStudio Training Tools工具链下精度调试部分的工具包,主要包括精度预检、溢出检测、精度比对和梯度监控等功能,目前适配PyTorch和MindSpore框架。具体参考文档

两种dump方式

1
2
3
4
5
6
7
8
9
10
11
12
13
# 导入工具的数据采集接口。
from msprobe.pytorch import PrecisionDebugger, seed_all

# 在模型训练开始前固定随机性。
seed_all()
# 在模型训练开始前实例化PrecisionDebugger
debugger = PrecisionDebugger(config_path='./config.json')

# 开启数据dump。
debugger.start(model=module)

# 关闭数据dump。
debugger.stop()

另一种方式直接save任意数据对象,参考文档

1
2
3
4
5
6
7
8
from msprobe.pytorch import PrecisionDebugger
# 在模型训练开始前实例化PrecisionDebugger
debugger = PrecisionDebugger(dump_path="dump_path", level="debug")

# xxx
debugger.save(x2, "x2", save_backward=True) # 调用save接口
# xxx
debugger.step() # 调用debugger.step用于区分step保存

可视化为excel

参考mstt文档

创建对比文件

1
2
3
4
{
"npu_path": "./npu_dump/debug.json",
"bench_path": "./bench_dump/debug.json"
}

执行 msprobe -f pytorch compare -i ./compare.json -o ./output

可视化为图节点

参考vllm文档

安装tensorborad插件 pip install tb-graph-ascend

创建compare.json

1
2
3
4
5
{
"npu_path": "./npu_dump",
"bench_path": "./bench_dump",
"is_print_compare_log": true
}

转换成对比图

1
msprobe -f pytorch graph -i ./compare.json -o ./output

启动tensorboard

1
tensorboard --logdir output --host localhost --port 6501  

icecream for debug

rich 库是icecream的上位替代

  • rich:功能更全:支持任意对象的详细信息,包括method; 支持log;支持进度条;支持打印堆栈。
  • rich:打印更华丽

pprint 也不错

pprint 是 Python 的 pprint 模块中的一个函数,全称是 pretty-print(漂亮打印)。它用于以更易读的格式打印数据结构,如字典、列表等。

1
2
from pprint import pprint
pprint(obj)
  • 优雅打印对象:函数名,结构体
  • 打印行号和栈(没用输入时
  • 允许嵌套(会将输入传递到输出
  • 允许带颜色ic.format(*args)获得ic打印的文本
  • debug ic.disable()and ic.enable()
  • 允许统一前缀 ic.configureOutput(prefix='Debug | ')
  • 不用每个文件import
1
2
3
4
5
6
7
from icecream import ic
ic(STH)

from icecream import install
install()

ic.configureOutput(prefix='Debug -> ', outputFunction=yellowPrint)

icecream 是实时打印

普通print不是实时的,可能会出现,代码顺序在后面的ic反而打印在print前面。为此需要print(xxx,flush=True)

prefix 打印代码位置和时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import datetime
import inspect
from icecream import ic

def ic_with_timestamp(*args):
# Get the current frame's information
frame = inspect.currentframe().f_back # Get the caller's frame
filename = frame.f_code.co_filename # File where the function is called
lineno = frame.f_lineno # Line number where the function is called

timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

# Format the output to include timestamp, file, and line number
return '\n\n%s %s:%d shaojieLog >| ' % (timestamp, filename, lineno)

# Configure icecream to use this custom output function
ic.configureOutput(prefix=ic_with_timestamp)

# Example usage
ic("This is a test message.")

prefix 添加时间

1
2
3
4
5
import datetime
def ic_with_timestamp(*args):
return '\n\n%s shaojieLog >| ' % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

ic.configureOutput(prefix=ic_with_timestamp)

打印ic间时间间隔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import datetime
import inspect
from icecream import ic

# Initialize a variable to store the time of the last ic call
last_ic_time = None
initial_ic_time = datetime.datetime.now() # Set the initial time when the script starts

# Define a custom function that prepends the time since the last ic call, file name, and line number
def ic_with_timestamp(*args):
global last_ic_time
current_time = datetime.datetime.now()

# Calculate the time difference if there was a previous ic call
if last_ic_time is not None:
time_diff = current_time - last_ic_time
time_diff_str = f" (+{time_diff.total_seconds():.2f}s)"
else:
time_diff_str = ""

# Calculate the time since the initial call
time_since_initial = current_time - initial_ic_time
time_since_initial_str = f" [Total time: {time_since_initial.total_seconds():.2f}s]"

# Update last_ic_time to the current time
last_ic_time = current_time

return f'\n\n{current_time.strftime("%Y-%m-%d %H:%M:%S")}{time_diff_str}{time_since_initial_str} shaojieLog |> '
ic.configureOutput(prefix=ic_with_timestamp)

torchrun等多进程环境,利用dist.rank==0来保证只有一个打印

1
2
3
4
5
6
7
8
# Disable icecream
ic.disable()

# This message will be hidden
ic("This message will NOT be shown")

# Re-enable icecream
ic.enable()

ic()的输出无法被tee的log文件捕获

这个问题与 icecream 库的 ic() 函数的默认输出机制有关。icecream 默认将输出发送到标准错误(stderr),而 tee 命令的默认行为是只捕获标准输出(stdout)。因此,ic() 的输出不会被 tee 捕获到。

要解决这个问题,你可以采取以下几种方式:

  1. 使用 ic() 输出到标准输出
  2. 你可以配置 icecream 的输出流,使其输出到标准输出,而不是默认的标准错误。这样,tee 就可以捕获 ic() 的输出。
1
2
3
4
from icecream import ic
import sys

ic.configureOutput(outputFunction=sys.stdout.write)

这样,ic() 的输出就会被发送到标准输出,然后可以被 tee 命令捕获到。

  1. tee 捕获标准错误和标准输出

你也可以让 tee 捕获标准错误(stderr)和标准输出(stdout),这样无需修改 icecream 的配置。

在你的命令中,可以使用如下方式:

1
python3.8 setup.py build bdist_wheel 2>&1 | tee compile.log

在这个命令中,2>&1 将标准错误重定向到标准输出,因此 tee 可以同时捕获两者。

  1. 使用 tee 捕获标准错误单独输出
如果你只想捕获标准错误的输出,并将其保存到日志文件,可以使用以下命令:

1
python3.8 setup.py build bdist_wheel 1>&2 | tee compile.log
或将 `stderr` 和 `stdout` 单独重定向:
1
python3.8 setup.py build bdist_wheel 2>compile.log

性能优化 与 可视化

定位 Python 中 setup.py 脚本运行缓慢的 热点,可以通过多种方式进行性能分析,具体步骤取决于你想了解的性能细节。以下是几种常见的方法来定位性能瓶颈。

方法 1: 使用 cProfile 进行性能分析

cProfile 是 Python 标准库中用于进行性能分析的工具。你可以用它来跟踪 setup.py 执行时的函数调用并找到性能瓶颈。

cProfile + snakeviz + gprof2dot

1
./gprof2dot.py -f pstats Diff.status | dot -Tpng -o ./output/Diff.png

1.1 使用 cProfile 分析 setup.py

你可以通过 cProfile 运行 setup.py 并生成分析报告:

1
python -m cProfile -o setup.prof setup.py install

这将运行 setup.py 并将性能分析结果保存到 setup.prof 文件中。

1.2 可视化分析报告

使用 pstats 或者第三方工具 snakeviz 来分析 setup.prof

  1. 使用 pstats 来查看分析结果:

    1
    python -m pstats setup.prof

    然后,你可以在 pstats 交互式界面中输入命令,比如:

    • sort cumtime 按总耗时排序。
    • stats 查看函数调用的分析结果。
  2. 安装 snakeviz 来生成Web图形化报告:

    1
    pip install snakeviz

    运行 snakeviz 来可视化分析结果:

    1
    snakeviz setup.prof # deploy to 127.0.0.1:8080

    这样可以生成一个图形化的界面,显示每个函数的执行时间以及调用关系,让你更直观地看到性能瓶颈。

  3. 使用 gprof2dot 生成调用关系图片:

    安装 gprof2dot 工具:pip install gprof2dot

    使用 gprof2dot 将 cProfile 生成的 output.prof 转换为 .dot 文件:gprof2dot -f pstats output.prof | dot -Tsvg -o output.svg

    这里的 -f pstats 表示输入的格式是 cProfile 生成的 pstats 文件。这个命令会将结果转换为 SVG 格式的火焰图,保存为 output.svg。

    打开生成的 SVG 文件,查看火焰图。

  4. 生成火焰图: flameprof

    1. 正常的火焰图说明了上到下的调用关系,倒置火焰图说明了底层最耗时的元素。
    2. python flameprof.py input.prof > output.svg
  5. 生成火焰图(有详细文件路径): flamegraph

    1. flameprof --format=log requests.prof | xxx_path/flamegraph.pl > requests-flamegraph.svg

方法 3: 使用 line_profiler 进行逐行性能分析

如果你想深入了解 setup.py 的某个函数或一组函数的逐行性能,可以使用 line_profiler 工具来分析代码的逐行执行时间。

3.1 安装 line_profiler

1
pip install line_profiler

3.2 添加装饰器

首先,在 setup.py 中找到你想要分析的函数,添加 @profile 装饰器(在 line_profiler 中的分析模式下使用):

1
2
3
@profile
def some_function():
# Your function code

3.3 运行 line_profiler

你可以使用 kernprof.py 来运行 setup.py 并生成逐行性能报告:

1
kernprof -l -v setup.py install

这将运行 setup.py 并生成一份逐行性能分析报告,显示每一行代码的耗时。

方法 4: 使用 Py-Spy 进行实时性能分析(推荐!!!)

Py-Spy 是一个 Python 的取样分析器,它可以在不修改代码的情况下对 Python 程序进行性能分析,并生成实时的性能报告。

py-spy top — xxx 有时会卡住

4.1 安装 Py-Spy

1
pip install py-spy

4.2 运行 Py-Spysetup.py 进行分析

你可以在执行 setup.py 的同时运行 Py-Spy 进行取样分析:

1
py-spy top -- python setup.py install

这会生成一个实时的报告,类似于 top 命令,显示当前正在运行的 Python 函数以及其消耗的 CPU 时间。

4.3 生成火焰图

如果你希望生成一个更直观的火焰图,可以使用 py-spy 生成火焰图文件:

1
py-spy record -o profile.svg -- python setup.py install

然后你可以打开 profile.svg 文件,查看一个交互式的火焰图,清晰展示函数调用的时间分布。

方法 5: 使用 strace 分析系统调用

如果 setup.py 涉及大量的 I/O 操作(比如读写文件或安装依赖包),可能是这些操作导致了性能瓶颈。你可以使用 strace 来分析 setup.py 的系统调用,找到 I/O 操作的瓶颈。

1
strace -tt -T -o strace.log python setup.py install
  • -tt 选项会显示每个系统调用的时间戳。
  • -T 会显示每个系统调用耗时。
  • -o 将结果输出到 strace.log 文件中。

通过查看 strace.log,你可以找出系统调用中哪些操作耗时过长。


总结

  1. 使用 cProfilePy-Spy 进行函数级别的性能分析,找出执行慢的函数。
  2. 如果需要更细粒度的逐行分析,使用 line_profiler 来分析慢的部分。
  3. 如果怀疑是 I/O 问题,用 strace 来检查系统调用。
  4. 使用 time 在脚本中插入计时代码,快速定位长时间的执行步骤。

这些工具可以帮助你定位和修复 setup.py 运行缓慢的热点。

CI

doctest

函数的单元测试

虚拟环境venv

1
2
3
4
5
6
7
8
python3 -m venv name

#在Windows上,运行:
name\Scripts\activate.bat # poweshell运行activate.ps1
#在Unix或MacOS上,运行:
source name/bin/activate
#(这个脚本是为bash shell编写的。如果你使用 csh 或 fish shell,你应该改用 activate.csh 或 activate.fish 脚本。)
python3 setup.py install

实践

  1. 并行调用shell命令,超时kill
  2. 基于Pipe的自定义多进程进度条

数据快速写入和读取文件

任意变量使用pickle

1
2
3
4
5
# 使用二进制
with open('my_dict.json', 'wb') as f:
pickle.dump(my_dict, f)
with open('my_dict.json', 'rb') as f:
loaded_dict = pickle.load(f)

可以序列化的使用json

1
2
3
4
5
6
7
8
import json
# 将 dict 保存为 JSON 格式
with open('my_dict.json', 'w') as f:
json.dump(my_dict, f)

# 加载 dict
with open('my_dict.json', 'r') as f:
loaded_dict = json.load(f)

多个变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 将多个变量组织成字典或列表
data = {
"scaAvgTime": scaAvgTime,
"var2": var2,
"var3": var3
}

result_file = "result.json"

# 将数据写入JSON文件
with open(result_file, "w") as f:
json.dump(data, f)

# 读取JSON文件
with open(result_file, "r") as f:
data = json.load(f)

# 获取保存的变量值
scaAvgTime = data["scaAvgTime"]
var2 = data["var2"]
var3 = data["var3"]

参考文献

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

Scons

Scons

  • SCons is a software construction tool that can be used as an alternative to traditional build systems like Make and CMake.
  • It is a Python-based build tool that provides a convenient and flexible way to define and manage the build process for software projects, including C++ programs.

Scons VS cmake

  1. 基于python语言的构建工具,对开发者来说过度自然,简单,no need to learn domain-specific language like cmake

其余cmake有的, Scons 也有。

  1. cross-paltform,
  2. SCons has built-in support for dynamic dependency analysis, meaning it can automatically detect changes in source files and rebuild only what’s necessary. This can result in faster builds for large projects.

Project structure

  1. Sconstruct python file as compile entry

framework grammar

  1. add option for scons command

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    AddOption('--buildDir', 
    dest='buildDir',
    type='string',
    default="build/",
    # default=False,
    nargs=1,
    action='store', # meaning save the string
    # or action='store', meaning True or false
    metavar='DIR',
    help='Base build directory'
    )
    baseBuildDir = GetOption('buildDir')
  2. add sub scons config file and build result path using variant_dir

    1
    env.SConscript("src/SConscript", variant_dir=buildDir, exports= {'env' : env.Clone()})     
  3. achive debug mode

    using scons debug=1 command.

    1
    2
    3
    4
    env = Environment()
    debug = ARGUMENTS.get("debug", 0)
    if int(debug):
    print "in debug mode"

main construct grammar

  1. Define the Build Environment:
    In the SConstruct file, define the build environment by creating an Environment object. You can specify compiler options, flags, include paths, library paths, and other build settings within this object.

    1
    2
    env = Environment(CXX='g++', CCFLAGS=['-O2', '-Wall'], CPPPATH=['include'], LIBPATH=['lib'])
    libEnv = env.Clone()
  2. Specify Source Files and Targets:
    Define the source files for your C++ program and specify the target(s) you want to build using the Program() function.

    1
    2
    3
    4
    source_files = ['main.cpp', 'util.cpp', 'other.cpp']
    # or select the src files
    Object('hello.cpp')
    program = env.Program(target='my_program', source=source_files)

    In this example, main.cpp, util.cpp, and other.cpp are the source files, and my_program is the name of the target executable.

  3. static or dynamic lib

1
2
3
4
5
6
# static
Library("t", Glob("src/*.cpp"))
# dynamic
source = Glob("src/*.cpp")
SharedLibrary("t", source)
Program(["hello.cpp"], LIBS=["t"], LIBPATH=".")
  1. execute command during compilation
  • this is usually to print info
  • The command is executed when any of the specified dependencies (allSrcs, ".git/index", or "SConstruct") change.
1
2
3
4
5
6
7
8
9
10
env.Command(
target='bar.out',
source='bar.in',
action=["rm -f $TARGET", "$BAR_BUILD < $SOURCES > $TARGET"],
ENV={'PATH': '/usr/local/bin/'},
)
env.Command(
versionFile,
allSrcs + [".git/index" "SConstruct"],
'printf "#define ZSIM_BUILDDATE \\"`date "+%Y-%m-%d %T"`\\"\\n#define ZSIM_BUILDVERSION \\"`python misc/getver.py`\\"" >>' + versionFile)

Command

1
2
scons -c  # Clean
scons debug=1 # Rebuild using `SConstruct` file in debug mode

scons-project analysis

TODO: multipim how to add a singel head file during compilation process.

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

https://scons.org/doc/production/PDF/scons-man.pdf

LinuxFolderInstall

system folders structure

根据教程

  1. /etc:是 Etcetera(等等) 的缩写,这个目录用来存放所有的系统管理所需要的配置文件和子目录。
    1. 这个是系统中的配置文件,如果你更改了该目录下的某个文件可能会导致系统不能启动。各种服务(ssh,apache2,nfs)的配置文件
  2. /lib:是 Library(库) 的缩写这个目录里存放着系统最基本的动态连接共享库,其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库。
  3. /opt:是 optional(可选) 的缩写,这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。
  4. /usr:是 unix shared resources(共享资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录。It’s install for all user.
  5. /var:是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件

sudo install path

TODO: during the application install, which locations those app used?

I guess it’s usr/bin or /include , opt and /lib

  1. GNU G++
  2. cuda
  3. intel one-api

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

0 Overview

工作外的内容聚焦于几个方面:

  1. 家庭、环境和高雅娱乐
  2. 额外职业技能的培养
  3. 额外的计算机技术的使用(网络和主页网站配置)
  4. 各种设备(主要是科技设备)
  5. 影视相关
  6. 游戏相关

3.3 EfficientWorkLearning

科研工作的目标

  1. 从底层的优化做起,自底向上的。对整个AI训练的难点有个全面细致的认知。
  2. 或者从应用的问题出发建模,从上到下有个全面的认知。
  3. 往架构师的方向努力

淡而不厌,简而文,温而理,知远之近,知风之自,知微之显,可与人德矣。

科研学习

狠狠吸收

华为实习部门的都是手握A会的博士大佬。本人望尘莫及,我会狠狠吸收的。

科研的长远与犀利的眼光

  1. 如何识别伪装信息 和被包装的工作
  2. 明确理论目标上限,当前的差距,实现路径

研究理解与实现

  1. 跨领域知识:公众号,订阅
  2. follow前沿论文:follow学者, 公众号,
  3. 实际问题、需求的发现
    1. 技术论证:理论上限,可行方案
    2. 独立任务分割
    3. 高效实现(解决问题)

科研工作的全局观念

  • 自顶向下的设计规划、工作全局观 (从目标、需求、愿景出发。不断细化实现点)
    • 顶:论文全流程思路图(构思与其余工作的对比):当前实现和问题、兼顾创新性的方法 和 设计目标期望效果。
    • 中:代码实现逻辑框架图
    • 底:代码语言具体实现:高内聚低耦合的重要性,解耦,拆分,这样就容易重构了。独立的微服务
  • 自底向上的知识积累 与研究方法提出
    • 底:领域的基础知识
    • 中:领域的主流方法和解决方案
    • 顶:行业痛点和有待补完的领域空白。
  • 当两者相联,目标才能顺利的达成,

高效学习的过程中注意点

  1. 学习的优先级:用20%时间先掌握80%的基础知识或者感兴趣的关键
    1. 难的问题可以讨论合作得出
  2. 提问式主动学习:不是被动学习,尝试通过提问、讨论、教授他人等方式来加深理解和巩固所学内容。
  3. 合作学习: 不仅能头脑风暴idea。对概念的理解,表达能力,心理健康有好处
  4. 交叉与分割学习: 概念文字、视频和案例分析交叉理解。长时间执行单一学习会枯燥,效率降低。切换学习一些新鲜东西:每日关注LLM的有趣实现。
  5. 理论实践交融:实际运行或者编写测试代码运行来深入理解
  6. 持续反馈与评估:每天每周对学习的进度和效果进行分析、来调整学习计划和研究方向。可以遵循STAR 法则。

具体研究点的克服

体系结构量化分析方法,重点就在于量化分析开销,比较然后进行tradeoff。当前前提是你要有基本的相关概念。

具体知识来源的优先级,或者说如何使用搜索引擎:

  1. 明白原理,设计实验,实际机器测量
    1. 认知概念,理解 (图解 >> 列表对比 >> 文字list >> 大段描述)
  2. 阅读相关的论文以及书籍
  3. 国内大佬的博客和大论文
  4. 国外论坛Stack Overflow > 国内知乎 > 博客园 > csdn > 其他
    1. 资料的来源(论文 >> 官方文档 >> 英文博客 >> 高质量中文资料)

在理解概念,量化了具体场景的数值后,就可以开心进行tradeoff了。

  1. 思维导图、摘要,来理清概念 和思路
  2. 结合PPT 数据与图表展示效果

注意项目的可读性和可拓展性一般与性能是不兼容的。这取决于项目的checkpoint/middleValue的保存,在性能优化时往往会消除中间变量。这样会导致代码的可读性和可拓展性下降。

check-point的合理设置

  1. 合理的检查点,既是阶段性的成果,又能在此衍生出无限的可能
  2. 需要能高效的复现与重构

关于如何解决困难

困难的定义可以基于以下几个要素进行评估:

  1. 个人技能能力:困难的程度可以取决于个人所具备的技能和能力水平。对于一个人来说,某项任务可能很容易,而对另一个人来说可能很困难,这取决于他们的专业知识、经验和技能。如果一个人已经具备了必要的知识和技能,那么他们可能更容易应对困难任务。相反,如果缺乏必要的知识和技能,任务就会更具挑战性。
  2. 任务量评估:任务的规模和复杂性也是评估困难程度的重要因素。任务量的多少以及任务本身的复杂性(比如需要解决的问题、涉及的步骤等)会对困难程度产生影响。
    1. 多人合作:效率会由于沟通同步而减半
    2. 量化分析加深理解:对于某些任务,特别是涉及复杂问题解决或决策制定的情况,进行量化分析可以加深对问题本质的理解。这种理解的深度也会对困难程度产生影响,因为解决关键核心会对整个任务的理解的评估进行重大修正。
    3. 对未知领域的任务量评估,会随着了解而变得准确。(这不是产品经理的工作吗?
  3. 时间的紧迫程度:完成任务所要求的时间紧迫程度也是评估困难程度的因素之一。如果任务需要在很短的时间内完成,那么它可能会被认为是更具挑战性和困难的。

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

3.2 taskPriority

工作优先级四象限(优先级矩阵)

  • 基础版本(艾森豪威尔)
    • 对于紧急又重要的事情马上做。如果这类事情过多,那就想办法规划时间,减少此类事情。
    • 对于重要但不紧急的事情计划做。尽可能地把时间花在重要但不紧急的事情上,这样才能减少产生重要且紧急的工作量。
    • 对于紧急但不重要的事情授权做。处理原则是授权,让别人去做。
    • 最后对于不重要不紧急的事情减少做
  • 额外的维度
    • 依赖关系:工作中显露的潜在工作会打断工作的交付,砍半降低交付效率
      • 尽可能全面

拓展版本

  • 当前工作优先级评估由 四个连续值维度 + 三个逻辑指标 组成
  • 优先级评估的时候不止是自己的视角,还有别人的视角(别人在心里的权重 * 重要性 * 紧迫性)。其中别人在心里的权重,一般来说: 父母、爱人、子女 > 领导 > 我

四个连续值维度

  • 喜好程度
  • **估计工作量(投入收益比)**。这一点其实很难量化,自己都没有做怎么知道,只能横向收集友商的数据。
  • 紧急的纬度由DDL时间确定,这点可以和工作量指标互动。
  • 重要性由以下几点确定,都能提高对工作的重要性评价:
    • 增加对工作的喜爱程度的任务: 高效插件与可视化工具的学习使用,
    • 提高工作效率的任务:学习基础知识,
    • 减少长期的工作量:学习、制造和使用轮子;自动化工作。

三个逻辑指标

  • 工作间依赖关系
  • 专注度要求(与疲劳度互补,清醒时才能做思考工作,一天工作后也能进行的简单工程工作)
  • 是否属于未知的领域。(与专注度要求不是正交的关系,是集合的包含关系。需要动脑的事情,肯定是未知的)

经常遇到的实际情况

相同的指标

  1. 紧急程度:不紧急。我一般会比较焦虑,所以工作会较早立项。
  2. 重要程度:我认为重要的才会主动去做,无论是对眼前的毕业考核,还是长远的考虑
  3. 喜好程度:我认为有趣的才会主动去做
  4. 工作间的依赖关系:我会遵守逻辑

不同的指标:

  1. 是否未知:阅读信息的需求
  2. 是否需要专注: 思考需求

两者结合:纯思考 > 阅读加稍微思考 > 初步的阅读收集信息 > 纯机械工作

任务周期内:时间分配和执行顺序

之前的任务优先级评判,都是从完成任务的角度考虑。但是实际情况是每个任务都需要很久(许多任务周期)才能完成。

按照优先级的指标,例如:

紧急性(3) 重要性(3) 喜好(1) 工作量(3) 总分 分配 要求
report 3 0 0 0 3 一天欠 2
thesis 3 3 0 3 9 两天多 3
AI 2 2 1 2 7 两天欠 1
OpenCL 2 1 1 1 5 一天多 1
web 1 0 0 1 2 一天欠 1
Summary 26
  1. 紧急性: 迫在眉睫(几天), 稍等几周,稍等几月,半年一年,可有可无
  2. 重要性: (当下)重大转折,(潜在)深远影响, 一年内小方向,与我无瓜
  3. 喜好:特别喜欢,有点意思,毫无波澜,有点厌恶
  4. 预估工作量(专心情况下):半年以上,一个季度,一个月,一周
  5. 要求(专注度):纯思考 > 阅读加稍微思考 > 初步的阅读收集信息 > 纯机械工作 (3~0)

注意

  • 工作安排 “必须做”占 40%,“愿意做”限制在 30% 左右,剩下30%处理出现的未发现的依赖任务和计划外工作。
  • 涉及到合作的工作:要与对方商量好,自己的选择(为什么把你鸽了,不是)

理想中的二维可视化细节

  • 科研工作与生活各自独立一张图。处理的时间段不同。
  • 横坐标是时间DDL表示紧急程度Urgency,纵坐标是重要性Impact(代表能增强自身和造轮子,还是繁琐小事)
    • 横坐标会随时间自动移动,
    • 标记出四块或者9块颜色
  • 节点可以可视化的部分
    • 颜色深浅表示喜好程度、投入收益比
    • 大小表示工作量绝对值(难易程度)
      • 会根据每日的任务自动调整
      • 甘特图 Gantt:的长条状,中间塞进度条的百分比实现。
    • 和连线表示工作依赖关系
    • 特殊颜色/形状 表示设置里程碑(北极星)任务,完成后自己会收获什么(能力属性标签)
  • 节点额外的属性值(不可视化)
    • 任务的风险
    • 需要的合作者,资源
    • DoD (完成标准,验收标准)
  • 根据公式和数据, 计算工作的优先级并给出推荐。
    • 考虑WIP(Work in Progress)
  • 实现日历功能
  • 为了能激励自身,引入信息增长统计
    • 过去一周/月/季度/年,完成的各类型的Task
      • 引入勤劳值(工作量统计),和收获值(能力增长统计)
    • 能力属性标签, 数值是否随时间衰减

已有的优先级矩阵产品

  • ducalis
  • 另一种维度,将紧急程度与工作量交换:pic

团队合作的优先级

团队合作中,解决问题的策略与优先级

对象:领导、部门的同事团队( 其余部门的同事团队),个人主体。

思考的基础与前提(多沟通,深分析,找关键):

  1. 找到问题的关键,并提出实用有效的方法
  2. 问题考虑全面,目光长远,设计方法可持续

情形:

  1. 别人遇到问题求助
    1. 授人以鱼不如授人以渔
    2. 如果有其余需求,归纳到最后一点统计决策
  2. 自己遇到问题
    1. 研究瓶颈
      1. 在充分的调研与汇总整理后,向同事或者上级咨询与求助
    2. 两难抉择: 返工的bug修复,新功能,新业务,新研究方向与现有的工作的时间冲突
      1. 工作优先级四象限:根据重要性、紧急程度、喜好程度、工作量(投入收益比)与依赖关系分类
      2. 要与提出需求的对方商量好,解释自己的选择和困难

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

3 EfficientJumpingRunning

jumping the branch task

五大阻碍工作完成时间的罪魁祸首

  1. 过多的Work in Progress
    1. 太多WIP会导致很多问题:交付延误、品质下降和员工情绪恶化
    2. 利特尔定律
      $$平均周期时间 =\frac{平均WIP量 }{平均产出量 }$$
  2. 未知的依赖工作
    1. 常见依赖关系有3种:

      架构(软件和硬件):一个组件的变更可能破坏另一个组件导致它停止运行
      专业知识:从专家那里获得建议或帮助(需要怎样做某事)
      活动:直到活动完成才能取得进展

  3. 计划外工作(妨碍你完成某事或导致你无法实现里程碑的干扰事项)
  4. 优先级冲突(相互竞争的项目和任务。当你不确定做什么事情是最重要的时候,就会加剧这种冲突)
  5. 被忽视的工作(技术债)

如何相互影响

  1. 信念/意志确实很重要
    1. 强烈的信念能让你的工作迈出坚实的第一步,而且每一步都走得是否有力
    2. 但是前提是你要十分明确努力的方向,对工作的不自信会减半工作热情。
      1. 工作的优先级冲突,这将导致过多的WIP,从而导致更长的周期时间。
  2. 明确任务的优先级,并分阶段、逐步击破是最好的选择。

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

2: Courage to move on

Dilemma 困境

现实中的困难挫折、不如意、或者突如其来的变故/变化 会消磨和摧毁人的信念,让人变得失望难过。想逃避或者麻木自己,通常会在幻想 或者虚拟世界里寻求解脱。

Theoritical Foundation

  1. World is crucial. No time to sleep and waste more in virtual world
    1. just some happy and painless
  2. In virtual life, the more you pay, the more you lose.
    1. 逃避收获的事片刻的精神释放和快乐,但是带来的更chaos of real life

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。