以pytorch pipeline并行为例,分析各kernel的耗时占比及性能瓶颈

news2024/12/29 12:59:31

以pytorch pipeline并行为例,分析各kernel的耗时占比及性能瓶颈

  • 1.生成pipeline并行的测试代码
  • 2.pipeline profing
  • 3.生成nsys2json.py代码
  • 4.将nsys sqlite格式转chrome json格式
  • 5.生成耗时成分统计代码
  • 6.统计耗时成分
  • 7.耗时成分如下:
  • 8.查看GPU PCIE链路状态
  • 9.链路状态如下
  • 10.Nsight Compute查看Timeline

以下DEMO演示了,pipeline多卡并行时,如何分析各kernel的耗时占比
如果nccl kernel占比过大,说明GPU有效利用率不高
主要步骤如下:

  • 1.nsys profile 运行4卡pipeline并行demo
  • 2.用开源的nsys2json.py将nsys的sqlite-schema转成chrome event格式
  • 3.分析chrome event格式,按设备统计各kernel的耗时占比

1.生成pipeline并行的测试代码

tee pp_demo_fps.py <<-'EOF'
import os
import torch
from torch import nn
import torch.nn.functional as F
import numpy as np
import torch.distributed as dist
from torch.distributed import ReduceOp
import threading
import queue
import time
import argparse
import nvtx
 
class FeedForward(nn.Module):    
    def __init__(self,hidden_size,ffn_size):
        super(FeedForward, self).__init__()
        self.fc1 = nn.Linear(hidden_size, ffn_size,bias=False)
        self.fc2 = nn.Linear(ffn_size, hidden_size,bias=False)    
    def forward(self, input):
        with nvtx.annotate(f"fc1"):
            out=self.fc1(input)
        with nvtx.annotate(f"fc2"):
            out=self.fc2(out)          
        return out
  
parser = argparse.ArgumentParser(description="")
parser.add_argument('--hidden_size', default=512, type=int, help='')
parser.add_argument('--ffn_size', default=1024, type=int, help='')
parser.add_argument('--seq_len', default=512, type=int, help='')
parser.add_argument('--batch_size', default=8, type=int, help='')
parser.add_argument('--world_size', default=4, type=int, help='')
parser.add_argument('--device', default="cuda", type=str, help='')
parser.add_argument('--chunk_size', default=1, type=int, help='')
    
args = parser.parse_args()
hidden_size = args.hidden_size
ffn_size = args.ffn_size
seq_len = args.seq_len
batch_size = args.batch_size
world_size = args.world_size
device = args.device
chunk_size = args.chunk_size
   
def tp_mode():
  torch.random.manual_seed(1)
  dist.init_process_group(backend='nccl')
        
  world_size = torch.distributed.get_world_size()
  rank=rank = torch.distributed.get_rank()
  local_rank=int(os.environ['LOCAL_RANK'])
      
  torch.cuda.set_device(local_rank)
  device = torch.device("cuda",local_rank)
   
  model = FeedForward(hidden_size,ffn_size)
  model.eval()
  input = torch.rand((batch_size, seq_len, hidden_size),dtype=torch.float32).half().to(device)
  model=model.half().to(device)
   
   
  chunks=torch.split(input,chunk_size,dim=0)
   
  for epoch in range(32):
    with nvtx.annotate(f"rank:{rank} epoch:{epoch}"):
      all_output=[]
      snd_reqs=None
    
      for idx,chunk in enumerate(chunks):
        with nvtx.annotate(f"chunk:{idx}"):
          with nvtx.annotate(f"wait_snd"):
            if snd_reqs:
              snd_reqs.wait()
            snd_reqs=None
          if rank==0:
            with nvtx.annotate(f"infer"):
              out=model(chunk)
          else:
            with nvtx.annotate(f"recv"):
              torch.distributed.recv(chunk,rank-1)
            with nvtx.annotate(f"infer"):
              out=model(chunk)
          if rank==world_size-1:
            all_output.append(out.clone())
          else:
            with nvtx.annotate(f"isend"):
              snd_reqs = torch.distributed.isend(out,rank+1)
    
      if rank==world_size-1:
        with nvtx.annotate(f"concat"):
          out=torch.cat(all_output,dim=0)
   
if __name__ == "__main__":
  num_gpus = int(os.environ["WORLD_SIZE"]) if "WORLD_SIZE" in os.environ else 1
  is_distributed = num_gpus > 1
  if is_distributed:
    tp_mode()
EOF

2.pipeline profing

export NCCL_DEBUG=info
export NCCL_SOCKET_IFNAME=ens8
export NCCL_IB_DISABLE=1

rm cuda_profing_report.* -f
nsys profile --stats=true -o cuda_profing_report.nsys-rep -f true -t cuda,nvtx --gpu-metrics-device=0,1,2,3 \
        torchrun -m --nnodes=1 --nproc_per_node=4 pp_demo_fps --hidden_size 512 \
        --ffn_size 4096 --seq_len 512 --batch_size 16 --world_size 4 --chunk_size 8

3.生成nsys2json.py代码

tee nsys2json.py <<-'EOF'
import sqlite3
import argparse
import json
from pathlib import Path
import re
from collections import defaultdict

_PID_TO_DEVICE = None

# Code adapted from https://raw.githubusercontent.com/chenyu-jiang/nsys2json/main/nsys2json.py

def parse_args():
    parser = argparse.ArgumentParser(description='Convert nsight systems sqlite output to Google Event Trace compatible JSON.')
    parser.add_argument("-f", '--filename', help="Path to the input sqlite file.", required=True)
    parser.add_argument("-o", "--output", help="Output file name, default to same as input with .json extension.")
    parser.add_argument("-t", "--activity-type", help="Type of activities shown. Default to all.", default=["kernel", "nvtx-kernel"], choices=['kernel', 'nvtx', "nvtx-kernel", "cuda-api"], nargs="+")
    parser.add_argument("--nvtx-event-prefix", help="Filter NVTX events by their names' prefix.", type=str, nargs="*")
    parser.add_argument("--nvtx-color-scheme", help="""Color scheme for NVTX events.
                                                    Accepts a dict mapping a string to one of chrome tracing colors.
                                                    Events with names containing the string will be colored.
                                                    E.g. {"send": "thread_state_iowait", "recv": "thread_state_iowait", "compute": "thread_state_running"}
                                                    For details of the color scheme, see links in https://github.com/google/perfetto/issues/208
                                                    """, type=json.loads, default={})
    args = parser.parse_args()
    if args.output is None:
        args.output = Path(args.filename).with_suffix(".json")
    return args

class ActivityType:
    KERNEL = "kernel"
    NVTX_CPU = "nvtx"
    NVTX_KERNEL = "nvtx-kernel"
    CUDA_API = "cuda-api"

def munge_time(t):
    """Take a timestamp from nsys (ns) and convert it into us (the default for chrome://tracing)."""
    # For strict correctness, divide by 1000, but this reduces accuracy.
    return t / 1000.

# For reference of the schema, see
# https://docs.nvidia.com/nsight-systems/UserGuide/index.html#exporter-sqlite-schema
def parse_cupti_kernel_events(conn: sqlite3.Connection, strings: dict):
    per_device_kernel_rows = defaultdict(list)
    per_device_kernel_events = defaultdict(list)
    for row in conn.execute("SELECT * FROM CUPTI_ACTIVITY_KIND_KERNEL"):
        per_device_kernel_rows[row["deviceId"]].append(row)
        event = {
                "name": strings[row["shortName"]],
                "ph": "X", # Complete Event (Begin + End event)
                "cat": "cuda",
                "ts": munge_time(row["start"]),
                "dur": munge_time(row["end"] - row["start"]),
                "tid": "Stream {}".format(row["streamId"]),
                "pid": "Device {}".format(row["deviceId"]),
                "args": {
                    # TODO: More
                    },
                }
        per_device_kernel_events[row["deviceId"]].append(event)
    return per_device_kernel_rows, per_device_kernel_events

def link_pid_with_devices(conn: sqlite3.Connection):
    # map each pid to a device. assumes each pid is associated with a single device
    global _PID_TO_DEVICE
    if _PID_TO_DEVICE is None:
        pid_to_device = {}
        for row in conn.execute("SELECT DISTINCT deviceId, globalPid / 0x1000000 % 0x1000000 AS PID FROM CUPTI_ACTIVITY_KIND_KERNEL"):
            assert row["PID"] not in pid_to_device, \
                f"A single PID ({row['PID']}) is associated with multiple devices ({pid_to_device[row['PID']]} and {row['deviceId']})."
            pid_to_device[row["PID"]] = row["deviceId"]
        _PID_TO_DEVICE = pid_to_device
    return _PID_TO_DEVICE

def parse_nvtx_events(conn: sqlite3.Connection, event_prefix=None, color_scheme={}):
    if event_prefix is None:
        match_text = ''
    else:
        match_text = " AND "
        if len(event_prefix) == 1:
            match_text += f"NVTX_EVENTS.text LIKE '{event_prefix[0]}%'"
        else:
            match_text += "("
            for idx, prefix in enumerate(event_prefix):
                match_text += f"NVTX_EVENTS.text LIKE '{prefix}%'"
                if idx == len(event_prefix) - 1:
                    match_text += ")"
                else:
                    match_text += " OR "

    per_device_nvtx_rows = defaultdict(list)
    per_device_nvtx_events = defaultdict(list)
    pid_to_device = link_pid_with_devices(conn)
    # eventType 59 is NvtxPushPopRange, which corresponds to torch.cuda.nvtx.range apis
    for row in conn.execute(f"SELECT start, end, text, globalTid / 0x1000000 % 0x1000000 AS PID, globalTid % 0x1000000 AS TID FROM NVTX_EVENTS WHERE NVTX_EVENTS.eventType == 59{match_text};"):
        text = row['text']
        pid = row['PID']
        tid = row['TID']
        per_device_nvtx_rows[pid_to_device[pid]].append(row)
        assert pid in pid_to_device, f"PID {pid} not found in the pid to device map."
        event = {
                "name": text,
                "ph": "X", # Complete Event (Begin + End event)
                "cat": "nvtx",
                "ts": munge_time(row["start"]),
                "dur": munge_time(row["end"] - row["start"]),
                "tid": "NVTX Thread {}".format(tid),
                "pid": "Device {}".format(pid_to_device[pid]),
                "args": {
                    # TODO: More
                    },
                }
        if color_scheme:
            for key, color in color_scheme.items():
                if re.search(key, text):
                    event["cname"] = color
                    break
        per_device_nvtx_events[pid_to_device[pid]].append(event)
    return per_device_nvtx_rows, per_device_nvtx_events

def parse_cuda_api_events(conn: sqlite3.Connection, strings: dict):
    pid_to_devices = link_pid_with_devices(conn)
    per_device_api_rows = defaultdict(list)
    per_device_api_events = defaultdict(list)
    # event type 0 is TRACE_PROCESS_EVENT_CUDA_RUNTIME
    for row in conn.execute(f"SELECT start, end, globalTid / 0x1000000 % 0x1000000 AS PID, globalTid % 0x1000000 AS TID, correlationId, nameId FROM CUPTI_ACTIVITY_KIND_RUNTIME;"):
        text = strings[row['nameId']]
        pid = row['PID']
        tid = row['TID']
        correlationId = row['correlationId']
        per_device_api_rows[pid_to_devices[pid]].append(row)
        event = {
                "name": text,
                "ph": "X", # Complete Event (Begin + End event)
                "cat": "cuda_api",
                "ts": munge_time(row["start"]),
                "dur": munge_time(row["end"] - row["start"]),
                "tid": "CUDA API Thread {}".format(tid),
                "pid": "Device {}".format(pid_to_devices[pid]),
                "args": {
                        "correlationId": correlationId,
                    },
                }
        per_device_api_events[pid_to_devices[pid]].append(event)
    return per_device_api_rows, per_device_api_events

def _find_overlapping_intervals(nvtx_rows, cuda_api_rows):
    mixed_rows = []
    for nvtx_row in nvtx_rows:
        start = nvtx_row["start"]
        end = nvtx_row["end"]
        mixed_rows.append((start, 1, "nvtx", nvtx_row))
        mixed_rows.append((end, -1, "nvtx", nvtx_row))
    for cuda_api_row in cuda_api_rows:
        start = cuda_api_row["start"]
        end = cuda_api_row["end"]
        mixed_rows.append((start, 1, "cuda_api", cuda_api_row))
        mixed_rows.append((end, -1, "cuda_api", cuda_api_row))
    mixed_rows.sort(key=lambda x: (x[0], x[1], x[2]))
    active_intervals = []
    result = defaultdict(list)
    for _, event_type, event_origin, orig_event in mixed_rows:
        if event_type == 1:
            # start
            if event_origin == "nvtx":
                active_intervals.append(orig_event)
            else:
                for event in active_intervals:
                    result[event].append(orig_event)
        else:
            # end
            if event_origin == "nvtx":
                active_intervals.remove(orig_event)
    return result

def link_nvtx_events_to_kernel_events(strings: dict,
                                      pid_to_device: dict[int, int],
                                      per_device_nvtx_rows: dict[int, list],
                                      per_device_cuda_api_rows: dict[int, list],
                                      per_device_cuda_kernel_rows: dict[int, list],
                                      per_device_kernel_events: dict[int, list]):
    result = {}
    for device in pid_to_device.values():
        event_map = _find_overlapping_intervals(per_device_nvtx_rows[device], per_device_cuda_api_rows[device])
        correlation_id_map = defaultdict(dict)
        for cuda_api_row in per_device_cuda_api_rows[device]:
            correlation_id_map[cuda_api_row["correlationId"]]["cuda_api"] = cuda_api_row
        for kernel_row, kernel_trace_event in zip(per_device_cuda_kernel_rows[device], per_device_kernel_events[device]):
            correlation_id_map[kernel_row["correlationId"]]["kernel"] = kernel_row
            correlation_id_map[kernel_row["correlationId"]]["kernel_trace_event"] = kernel_trace_event
        for nvtx_row, cuda_api_rows in event_map.items():
            kernel_start_time = None
            kernel_end_time = None
            for cuda_api_row in cuda_api_rows:
                if "kernel" not in correlation_id_map[cuda_api_row["correlationId"]]:
                    # other cuda api event, ignore
                    continue
                kernel_row = correlation_id_map[cuda_api_row["correlationId"]]["kernel"]
                kernel_trace_event = correlation_id_map[cuda_api_row["correlationId"]]["kernel_trace_event"]
                if "NVTXRegions" not in kernel_trace_event["args"]:
                    kernel_trace_event["args"]["NVTXRegions"] = []
                kernel_trace_event["args"]["NVTXRegions"].append(nvtx_row["text"])
                if kernel_start_time is None or kernel_start_time > kernel_row["start"]:
                    kernel_start_time = kernel_row["start"]
                if kernel_end_time is None or kernel_end_time < kernel_row["end"]:
                    kernel_end_time = kernel_row["end"]
            if kernel_start_time is not None and kernel_end_time is not None:
                result[nvtx_row] = (kernel_start_time, kernel_end_time)
    return result

def parse_all_events(conn: sqlite3.Connection, strings: dict, activities=None, event_prefix=None, color_scheme={}):
    if activities is None:
        activities = [ActivityType.KERNEL, ActivityType.NVTX_CPU, ActivityType.NVTX_KERNEL]
    if ActivityType.KERNEL in activities or ActivityType.NVTX_KERNEL in activities:
        per_device_kernel_rows, per_device_kernel_events = parse_cupti_kernel_events(conn, strings)
    if ActivityType.NVTX_CPU in activities or ActivityType.NVTX_KERNEL in activities:
        per_device_nvtx_rows, per_device_nvtx_events = parse_nvtx_events(conn, event_prefix=event_prefix, color_scheme=color_scheme)
    if ActivityType.CUDA_API in activities or ActivityType.NVTX_KERNEL in activities:
        per_device_cuda_api_rows, per_device_cuda_api_events = parse_cuda_api_events(conn, strings)
    if ActivityType.NVTX_KERNEL in activities:
        pid_to_device = link_pid_with_devices(conn)
        nvtx_kernel_event_map = link_nvtx_events_to_kernel_events(strings, pid_to_device, per_device_nvtx_rows, per_device_cuda_api_rows, per_device_kernel_rows, per_device_kernel_events)
    traceEvents = []
    if ActivityType.KERNEL in activities:
        for k, v in per_device_kernel_events.items():
            traceEvents.extend(v)
    if ActivityType.NVTX_CPU in activities:
        for k, v in per_device_nvtx_events.items():
            traceEvents.extend(v)
    if ActivityType.CUDA_API in activities:
        for k, v in per_device_cuda_api_events.items():
            traceEvents.extend(v)
    if ActivityType.NVTX_KERNEL in activities:
        for nvtx_event, (kernel_start_time, kernel_end_time) in nvtx_kernel_event_map.items():
            event = {
                "name": nvtx_event["text"],
                "ph": "X", # Complete Event (Begin + End event)
                "cat": "nvtx-kernel",
                "ts": munge_time(kernel_start_time),
                "dur": munge_time(kernel_end_time - kernel_start_time),
                "tid": "NVTX Kernel Thread {}".format(nvtx_event["tid"]),
                "pid": "Device {}".format(pid_to_device[nvtx_event["pid"]]),
                "args": {
                    # TODO: More
                    },
                }
            traceEvents.append(event)
    return traceEvents

def nsys2json():
    args = parse_args()
    conn = sqlite3.connect(args.filename)
    conn.row_factory = sqlite3.Row

    strings = {}
    for r in conn.execute("SELECT id, value FROM StringIds"):
        strings[r["id"]] = r["value"]
    
    traceEvents = parse_all_events(conn, strings, activities=args.activity_type, event_prefix=args.nvtx_event_prefix, color_scheme=args.nvtx_color_scheme)
    # make the timelines appear in pid and tid order
    traceEvents.sort(key=lambda x: (x["pid"], x["tid"]))
    
    for i in traceEvents:
        if i["name"] is None:
            i["name"]="null"    
    with open(args.output, 'w') as f:
        json.dump(traceEvents, f,indent=4)

if __name__ == "__main__":
    nsys2json()
EOF

4.将nsys sqlite格式转chrome json格式

python3 nsys2json.py -f cuda_profing_report.sqlite -o prof.json

5.生成耗时成分统计代码

tee paser_prof.py <<-'EOF'
import json
import re
import os
import sys
import numpy as np

with open(sys.argv[1],"r") as f:
    traceEvents=json.load(f)
traceEventsPerDevice={}
for event in traceEvents:
    pid=event["pid"]
    if pid not in traceEventsPerDevice:
        traceEventsPerDevice[pid]=[] 
    if event["cat"]=="cuda":
        epoch_str=event["args"]['NVTXRegions'][0]
        match = re.match(".*epoch:(\d*)", epoch_str)
        epoch=int(match.group(1))    
        #过滤掉warmup阶段
        if epoch>4:
            traceEventsPerDevice[pid].append((event["name"]+"_"+event["tid"],event["ts"],event["dur"],epoch))
for k,v in traceEventsPerDevice.items():
    v.sort(key=lambda x: x[1], reverse=False)
for device,v in traceEventsPerDevice.items():  
    print(f"-----------------------------{device}-----------------------------")
    totalDurPerKernel={}
    durPerKernel={}
    marginPerKernel={}
    for ev in v:
        name,ts,dur,epoch=ev
        if name not in totalDurPerKernel:
            totalDurPerKernel[name]=0
            durPerKernel[name]=[]
            marginPerKernel[name]={"beg":ts}
        totalDurPerKernel[name]+=dur
        durPerKernel[name].append(dur)
        marginPerKernel[name]["end"]=ts        
    
    total_percent=0
    for name,dur in sorted(totalDurPerKernel.items(), key=lambda d:d[1], reverse = True):
        total_dur=marginPerKernel[name]["end"]-marginPerKernel[name]["beg"]
        total_percent+=(dur/total_dur)
        print("{:7.2f} min:{:7.2f} max:{:7.2f} avg:{:7.2f} {}".format(
                dur/total_dur,
                np.min(durPerKernel[name]),
                np.max(durPerKernel[name]),
                np.mean(durPerKernel[name]),
                name))
    print("{:7.2f}".format(total_percent))
EOF

6.统计耗时成分

python3 paser_prof.py prof.json

7.耗时成分如下:

-----------------------------Device 0-----------------------------
   0.88 min:1481.39 max:3153.01 avg:2304.01 ncclKernel_SendRecv_RING_SIMPLE_Sum_int8_t_Stream 20
   0.16 min: 285.22 max: 661.51 avg: 438.63 Kernel_Stream 7
   0.14 min: 238.11 max: 601.51 avg: 372.28 ampere_fp16_s16816gemm_fp16_128x64_ldg8_f2f_tn_Stream 7
   1.18
-----------------------------Device 1-----------------------------
   0.69 min:1413.80 max:2148.40 avg:1815.43 ncclKernel_SendRecv_RING_SIMPLE_Sum_int8_t_Stream 24
   0.43 min: 611.49 max:1828.39 avg:1130.83 ncclKernel_SendRecv_RING_SIMPLE_Sum_int8_t_Stream 20
   0.12 min: 233.79 max: 748.87 avg: 319.80 ampere_fp16_s16816gemm_fp16_128x64_ldg8_f2f_tn_Stream 7
   0.11 min: 276.61 max: 448.48 avg: 289.69 Kernel_Stream 7
   1.35
-----------------------------Device 2-----------------------------
   0.64 min:1425.58 max:1902.06 avg:1669.10 ncclKernel_SendRecv_RING_SIMPLE_Sum_int8_t_Stream 24
   0.49 min: 625.38 max:2027.44 avg:1294.33 ncclKernel_SendRecv_RING_SIMPLE_Sum_int8_t_Stream 20
   0.12 min: 283.36 max: 324.23 avg: 309.24 Kernel_Stream 7
   0.10 min: 233.76 max: 273.22 avg: 257.85 ampere_fp16_s16816gemm_fp16_128x64_ldg8_f2f_tn_Stream 7
   1.34
-----------------------------Device 3-----------------------------
   0.78 min:1437.31 max:2588.53 avg:2040.18 ncclKernel_SendRecv_RING_SIMPLE_Sum_int8_t_Stream 20
   0.12 min: 323.55 max: 324.13 avg: 323.83 Kernel_Stream 7
   0.10 min: 269.47 max: 274.24 avg: 270.91 ampere_fp16_s16816gemm_fp16_128x64_ldg8_f2f_tn_Stream 7
   0.01 min:  28.22 max:  29.12 avg:  28.64 CatArrayBatchedCopy_Stream 7
   1.01

8.查看GPU PCIE链路状态

nvidia-smi --query-gpu="gpu_name,pcie.link.gen.current,pcie.link.width.current" --format=csv -i 0,1,2,3

9.链路状态如下

name, pcie.link.gen.current, pcie.link.width.current
NVIDIA GeForce RTX 3090, 1, 16
NVIDIA GeForce RTX 3090, 1, 16
NVIDIA GeForce RTX 3090, 1, 16
NVIDIA GeForce RTX 3090, 1, 16
  • 当前为GEN1 X16: 理论带宽4GB/s

10.Nsight Compute查看Timeline

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1607397.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么选择气膜建造室内球馆?

在当今社会&#xff0c;越来越多的人选择使用气膜来建造室内球馆。这一选择背后有着多重原因和优势。 灵活性和便捷性 气膜结构球馆具有极高的灵活性和便捷性。相较于传统的建筑方式&#xff0c;搭建气膜球馆所需的时间更短&#xff0c;而且审批流程更为简单。尽管气膜球馆被视…

OpenHarmony多媒体-mp4parser

简介 一个读取、写入操作音视频文件编辑的工具。 编译运行 1、通过IDE工具下载依赖SDK&#xff0c;Tools->SDK Manager->Openharmony SDK 把native选项勾上下载&#xff0c;API版本>10 2、开发板选择RK3568&#xff0c;ROM下载地址. 选择开发板类型是rk3568&#xf…

Linux读写文件

前言 学习了文件系统&#xff0c;就能理解为什么说Linux下一切皆文件。 语言层面的操作 在c语言的学习中我们可以使用fopen()函数对文件进行操作。 int main() {//FILE * fp fopen("./log.txt", "w");//FILE * fp fopen("./log.txt", "…

C++入门之类和对象(中)

C入门之类和对象(中) 文章目录 C入门之类和对象(中)1. 类的6个默认对象2. 构造函数2.1 概念2.2 特性2.3 补丁 3. 析构函数3.1 概念3.2 特性3.3 总结 4. 拷贝构造函数4.1 概念4.2 特性4.3 总结 1. 类的6个默认对象 如果一个类中什么都没有&#xff0c;那么这个类就是一个空类。…

【每日刷题】Day7

【每日刷题】Day7 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 2. 203. 移除链表元素 - 力扣&#xff08;…

介绍与部署 Zabbix 监控系统

目录 前言 一、监控系统 1、主流的监控系统 2、监控系统功能 二、Zabbix 监控系统概述 1、Zabbix 概念 2、Zabbix 主要特点 3、Zabbix 主要功能 4、Zabbix 监控对象 5、Zabbix 主要程序 6、Zabbix 监控模式 7、Zabbix 运行机制 8、Zabbix 监控原理 9、Zabbix 主…

elementui单个输入框回车刷新整个页面

<!-- 搜索 --> <el-form :model"queryParams" ref"queryForm" :inline"true"><el-form-item label"名称" prop"nameLike"><el-input v-model"queryParams.nameLike" placeholder"请输入…

呼叫系统的技术实现原理和运作流程,ai智能系统,呼叫中心外呼软交换部署

呼叫系统的技术实现原理和运作流程可以涉及多个组成部分&#xff0c;包括硬件设备、软件系统和通信协议。以下是一般情况下呼叫系统的技术实现原理和运作流程的概述&#xff1a; 硬件设备&#xff1a; 服务器&#xff1a;用于承载呼叫系统的核心软件和数据库。电话交换机&#…

PyTorch|保存及加载模型、nn.Sequential、ModuleList和ModuleDict

系列文章目录 PyTorch|Dataset与DataLoader使用、构建自定义数据集 PyTorch|搭建分类网络实例、nn.Module源码学习 pytorch|autograd使用、训练模型 文章目录 系列文章目录一、保存及加载模型&#xff08;一&#xff09;保存及加载模型的权重&#xff08;二&#xff09;保存及…

前端 - 基础 表单标签 - label 标签

# label 标签 其实不属于 表单标签名单经常和 表单标签 搭配使用。 # <label> 标签 为 input 元素 定义 标注&#xff08; 标签 &#xff09; 使用场景 # 其实说白&#xff0c;<label> 标签就是为了方便用户体验的,举例说明 就是说&#xff0c;如上示&am…

软件需求开发和管理过程性指导文件

1. 目的 2. 适用范围 3. 参考文件 4. 术语和缩写 5. 需求获取的方式 5.1. 与用户交谈向用户提问题 5.1.1. 访谈重点注意事项 5.1.2. 访谈指南 5.2. 参观用户的工作流程 5.3. 向用户群体发调查问卷 5.4. 已有软件系统调研 5.5. 资料收集 5.6. 原型系统调研 5.6.1. …

【深度学习】yolov5目标检测学习与调试

2024.4.15 -2024.4.16 完结 0.准备&&补充知识点 yolo检测算法可以实现目标检测、分割和分类任务。 项目仓库地址&#xff1a;https://github.com/ultralytics/yolov5 跟练视频&#xff1a;目标检测 YOLOv5 开源代码项目调试与讲解实战 lux下载视频神器&#xff1a;h…

【氮化镓】栅极漏电对阈值电压和亚阈值摆幅影响建模

本文是一篇关于p-GaN门AlGaN/GaN高电子迁移率晶体管&#xff08;HEMTs&#xff09;的研究文章&#xff0c;发表于《应用物理杂志》&#xff08;J. Appl. Phys.&#xff09;2024年4月8日的期刊上。文章的标题为“Analysis and modeling of the influence of gate leakage curren…

从智能家居到智能城市:物联网中的隐私和安全风险

随着科技的不断进步&#xff0c;智能设备和物联网&#xff08;IoT&#xff09;技术已经逐渐渗透到我们的生活中。从智能家居设备到智能城市的实现&#xff0c;这些设备和技术可以让我们的生活变得更加便捷和高效。但是&#xff0c;这些设备也带来了不可忽视的隐私和安全风险。 …

Windows(Win10、Win11)本地部署开源大模型保姆级教程

目录 前言1.安装ollama2.安装大模型3.安装HyperV4.安装Docker5.安装聊天界面6.总结 点我去AIGIS公众号查看本文 本期教程用到的所有安装包已上传到百度网盘 链接&#xff1a;https://pan.baidu.com/s/1j281UcOF6gnOaumQP5XprA 提取码&#xff1a;wzw7 前言 最近开源大模型可谓闹…

内外网文件摆渡系统,如何贯通网络两侧被隔断的工作流?

随着业务范围不断扩大&#xff0c;产生的数据体量越来越多&#xff0c;企业会采取网络隔离&#xff0c;对核心数据进行保护。网络隔离主要目的是保护企业内部的敏感数据和系统不受外部网络攻击的风险&#xff0c;可以通过物理或逻辑方式实现&#xff0c;例如使用防火墙、网闸、…

如何让指定 Windows 程序崩溃

一、为何要把人家搞崩溃呢 看到这个标题&#xff0c;大家可能觉得奇怪&#xff0c;为什么要让指定程序崩溃呢&#xff0c;难道是想作恶吗&#xff1f;&#x1f613; 哈哈&#xff0c;绝对不是&#xff0c;真实原因是这样的。如果大家用过 Windows 电脑&#xff0c;可能见过类…

正版四月惠,MarginNote _ BookxNote _ 白描优惠啦!会场软件 5 折起

我们的老朋友数码荔枝&#xff0c;最近开启了「正版四月惠」活动&#xff01;会场精选了一批高效办公软件和系统增强工具&#xff0c;快来看看有没有你期待的那一款吧&#xff5e; 会场商品低至 5 折&#xff0c;快把它们带回家&#xff1a; MarginNote 3&#xff1a;7 折价 4…

Linux 系统下的进程间通信 IPC 入门 「下」

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/IvPHnEsC6ZdIHaFL8Deazg 共享内存 我们在进程间传输比较大的数据块时&#xff0c;通常选用共享内存的方式。共享内存大小也是有限制的&#xff0…

python-django企业设备配件检修系统flask+vue

本课题使用Python语言进行开发。代码层面的操作主要在PyCharm中进行&#xff0c;将系统所使用到的表以及数据存储到MySQL数据库中&#xff0c;方便对数据进行操作本课题基于WEB的开发平台&#xff0c;设计的基本思路是&#xff1a; 前端&#xff1a;vue.jselementui 框架&#…