DGL中minibatch训练子图Prefetch到GPU中加速

2023-11-11

-----2022.04 update------
最近DGL官方提供了prefetch的功能,可以直接使用官方实现了:https://github.com/dmlc/dgl/pull/3665

导读: 这两天在研究怎么加速DGL上GNN的训练,使用line_profiler工具发现,除了forward和backward之外,最耗时的是CPU与GPU之间的数据传输(即mini batch训练时将当前batch的子图及对应的feature和label传输到GPU中)。因此尝试使用prefetch,希望在当前batch进行GPU计算的同时,将数据从CPU传到GPU。本文使用的例子是:https://github.com/dmlc/dgl/tree/master/examples/pytorch/ogb_lsc/MAG240M,该场景下在我的环境中速度大概提升了15%

  先看一下train.py中主要的耗时(注意这里为了对齐后面的prefetch,将pin_memory设置为了True。而且为了防止OOM,将batch size设为了512):

Wrote profile results to train.py.lprof
Timer unit: 1e-06 s

Total time: 2000.99 s
File: train.py
Function: train at line 128

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   128                                           @profile
   129                                           def train(args, dataset, g, feats, paper_offset):
   130         1         23.0     23.0      0.0      print('Loading masks and labels')
   131         1       2024.0   2024.0      0.0      train_idx = torch.LongTensor(dataset.get_idx_split('train')) + paper_offset
   132         1       2506.0   2506.0      0.0      valid_idx = torch.LongTensor(dataset.get_idx_split('valid')) + paper_offset
   133         1      28142.0  28142.0      0.0      label = dataset.paper_label
   134
   135         1         25.0     25.0      0.0      print('Initializing dataloader...')
   136         1         44.0     44.0      0.0      sampler = dgl.dataloading.MultiLayerNeighborSampler([15, 25])
   137         1        887.0    887.0      0.0      train_collator = ExternalNodeCollator(g, train_idx, sampler, paper_offset, feats, label)
   138         1         70.0     70.0      0.0      valid_collator = ExternalNodeCollator(g, valid_idx, sampler, paper_offset, feats, label)
   139         1         11.0     11.0      0.0      train_dataloader = torch.utils.data.DataLoader(
   140         1          3.0      3.0      0.0          train_collator.dataset,
   141         1          9.0      9.0      0.0          batch_size=args.batch_size,
   142         1          1.0      1.0      0.0          shuffle=True,
   143         1          1.0      1.0      0.0          drop_last=False,
   144         1          1.0      1.0      0.0          collate_fn=train_collator.collate,
   145         1          1.0      1.0      0.0          num_workers=4,
   146         1        311.0    311.0      0.0          pin_memory=True
   147                                               )
   148         1          3.0      3.0      0.0      valid_dataloader = torch.utils.data.DataLoader(
   149         1          2.0      2.0      0.0          valid_collator.dataset,
   150         1          1.0      1.0      0.0          batch_size=args.batch_size,
   151         1          1.0      1.0      0.0          shuffle=True,
   152         1          2.0      2.0      0.0          drop_last=False,
   153         1          2.0      2.0      0.0          collate_fn=valid_collator.collate,
   154         1          1.0      1.0      0.0          num_workers=2,
   155         1         76.0     76.0      0.0          pin_memory=True
   156                                               )
   157
   158         1         23.0     23.0      0.0      print('Initializing model...')
   159         1   11117937.0 11117937.0      0.6      model = RGAT(dataset.num_paper_features, dataset.num_classes, 1024, 5, 2, 4, 0.5, 'paper').cuda()
   160         1       1948.0   1948.0      0.0      opt = torch.optim.Adam(model.parameters(), lr=0.001)
   161         1        209.0    209.0      0.0      sched = torch.optim.lr_scheduler.StepLR(opt, step_size=25, gamma=0.25)
   162
   163         1          2.0      2.0      0.0      best_acc = 0
   164
   165         2         14.0      7.0      0.0      for _ in range(args.epochs):
   166         1        624.0    624.0      0.0          model.train()
   167         1       3195.0   3195.0      0.0          with tqdm.tqdm(train_dataloader) as tq:
   168         1        104.0    104.0      0.0              torch.cuda.synchronize()
   169         1          3.0      3.0      0.0              t0 = time.perf_counter()
   170      2174  176648528.0  81255.1      8.8              for i, (input_nodes, output_nodes, mfgs) in enumerate(tq):
   171      2173    5748739.0   2645.5      0.3                  mfgs = [g.to('cuda') for g in mfgs]
   172
   173                                                           # t = mfgs[0].srcdata['x'][100]
   174                                                           # tt = mfgs[-1].dstdata['y'][5]
   175      2173  389817972.0 179391.6     19.5                  x = mfgs[0].srcdata['x']  #除了GPU前后向计算,就是这里最耗时
   176      2173     389554.0    179.3      0.0                  y = mfgs[-1].dstdata['y']
   177      2173  542893145.0 249835.8     27.1                  y_hat = model(mfgs, x)
   178      2173    9109582.0   4192.2      0.5                  loss = F.cross_entropy(y_hat, y)
   179      2173    4741064.0   2181.8      0.2                  opt.zero_grad()
   180      2173  435564957.0 200444.1     21.8                  loss.backward()
   181      2173   16753289.0   7709.8      0.8                  opt.step()
   182      2173     288915.0    133.0      0.0                  acc = (y_hat.argmax(1) == y).float().mean()
   183      2173  197078022.0  90694.0      9.8                  tq.set_postfix({'loss': '%.4f' % loss.item(), 'acc': '%.4f' % acc.item()}, refresh=False)

  从上可以看出,除了forward和backward以外,耗时最长的是x = mfgs[0].srcdata['x']。这里并不是取特征x耗时长,实际是因为DGL中取特征是lazy的,当第一次取的时候才真正从CPU往GPU传数据,可以通过在前面调用一下mfgs[0].srcdata['x'][100]来验证,添加这一行之后就会变成改行代码耗时很长了。
  接下来尝试进行Prefetch,由于DGL Graph没有提供相应功能,这里只能退而求其次,将FeatureLabel这两种Tensor类型进行Prefetch。
可以参考:1.https://zhuanlan.zhihu.com/p/66145913
2.https://zhuanlan.zhihu.com/p/72956595
原理介绍:https://github.com/NVIDIA/apex/issues/304#issuecomment-493562789
  同时发现dgl.dataloading.async_transfer也提供了异步传输的功能(只能针对Tensor),因此可以较为方便的实现。

  实现后结果如下,可以发现总时间从2000s左右减少为了1650s左右,主要节省的就是x = mfgs[0].srcdata['x']这一行的时间。再次提醒,如果pin_memory设置为了False,是无法使用的。

Wrote profile results to train_prefetch.py.lprof
Timer unit: 1e-06 s

Total time: 1654.36 s
File: train_prefetch.py
Function: train at line 172

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   172                                           @profile
   173                                           def train(args, dataset, g, feats, paper_offset):
   202         1         22.0     22.0      0.0      print('Initializing model...')
   203         1    6740234.0 6740234.0      0.4      model = RGAT(dataset.num_paper_features, dataset.num_classes, 1024, 5, 2, 4, 0.5, 'paper').cuda()
   204         1       1917.0   1917.0      0.0      opt = torch.optim.Adam(model.parameters(), lr=0.001)
   205         1        208.0    208.0      0.0      sched = torch.optim.lr_scheduler.StepLR(opt, step_size=25, gamma=0.25)
   206
   207         1          2.0      2.0      0.0      best_acc = 0
   208
   209
   210         2          9.0      4.5      0.0      for _ in range(args.epochs):
   211         1   10178442.0 10178442.0      0.6          train_prefetcher = data_prefetcher(train_dataloader, dev_id=0)
   212         1    6792441.0 6792441.0      0.4          valid_prefetcher = data_prefetcher(valid_dataloader, dev_id=0)
   213         1       1083.0   1083.0      0.0          model.train()
   214         1       1976.0   1976.0      0.0          with tqdm.tqdm(train_prefetcher) as tq:
   215         1      54357.0  54357.0      0.0              torch.cuda.synchronize()
   216         1          5.0      5.0      0.0              t0 = time.perf_counter()
   217      2174  145970461.0  67143.7      8.8              for i, (input_nodes, output_nodes, mfgs, input_x, target_y) in enumerate(tq):
   218      2173    8052025.0   3705.5      0.5                  mfgs = [g.to('cuda') for g in mfgs]
   219
   220      2173  562793527.0 258993.8     34.0                  y_hat = model(mfgs, input_x)
   221      2173   10833457.0   4985.5      0.7                  loss = F.cross_entropy(y_hat, target_y)
   222      2173    4943455.0   2274.9      0.3                  opt.zero_grad()
   223      2173  461658294.0 212452.0     27.9                  loss.backward()
   224      2173   20994316.0   9661.4      1.3                  opt.step()
   225      2173     300364.0    138.2      0.0                  acc = (y_hat.argmax(1) == target_y).float().mean()
   226      2173  211057970.0  97127.5     12.8                  tq.set_postfix({'loss': '%.4f' % loss.item(), 'acc': '%.4f' % acc.item()}, refresh=False)

  完整代码如下:


#!/usr/bin/env python
# coding: utf-8
import ogb
from ogb.lsc import MAG240MDataset, MAG240MEvaluator
import dgl
import torch
import numpy as np
import time
import tqdm
import dgl.function as fn
import numpy as np
import dgl.nn as dglnn
import torch.nn as nn
import torch.nn.functional as F
import argparse


class data_prefetcher:
    def __init__(self, loader, dev_id):
        self.loader = iter(loader)
        self.dev_id = dev_id
        self.transfer = dgl.dataloading.AsyncTransferer(dev_id)
        self.preload()

    def __iter__(self):
        return self

    def preload(self):
        try:
            self.input_nodes, self.output_nodes, self.mfgs, self.input_x, self.target_y = next(
                self.loader)
        except StopIteration:
            self.input_nodes = None
            self.output_nodes = None
            self.mfgs = None
            self.input_x_future = None
            self.target_y_future = None
            return
        self.input_x_future = self.transfer.async_copy(self.input_x, self.dev_id)
        self.target_y_future = self.transfer.async_copy(self.target_y, self.dev_id)

    def __next__(self):
        input_nodes = self.input_nodes
        output_nodes = self.output_nodes
        mfgs = self.mfgs
        input_x_future = self.input_x_future
        target_y_future = self.target_y_future
        if input_x_future is not None:
            input_x = input_x_future.wait()
        else:
            raise StopIteration()
        if target_y_future is not None:
            target_y = target_y_future.wait()
        else:
            raise StopIteration()
        self.preload()
        return input_nodes, output_nodes, mfgs, input_x, target_y


class RGAT(nn.Module):
    def __init__(self, in_channels, out_channels, hidden_channels, num_etypes, num_layers, num_heads, dropout,
                 pred_ntype):
        super().__init__()
        self.convs = nn.ModuleList()
        self.norms = nn.ModuleList()
        self.skips = nn.ModuleList()

        self.convs.append(nn.ModuleList([
            dglnn.GATConv(in_channels, hidden_channels // num_heads, num_heads, allow_zero_in_degree=True)
            for _ in range(num_etypes)
        ]))
        self.norms.append(nn.BatchNorm1d(hidden_channels))
        self.skips.append(nn.Linear(in_channels, hidden_channels))
        for _ in range(num_layers - 1):
            self.convs.append(nn.ModuleList([
                dglnn.GATConv(hidden_channels, hidden_channels // num_heads, num_heads, allow_zero_in_degree=True)
                for _ in range(num_etypes)
            ]))
            self.norms.append(nn.BatchNorm1d(hidden_channels))
            self.skips.append(nn.Linear(hidden_channels, hidden_channels))

        self.mlp = nn.Sequential(
            nn.Linear(hidden_channels, hidden_channels),
            nn.BatchNorm1d(hidden_channels),
            nn.ReLU(),
            nn.Dropout(dropout),
            nn.Linear(hidden_channels, out_channels)
        )
        self.dropout = nn.Dropout(dropout)

        self.hidden_channels = hidden_channels
        self.pred_ntype = pred_ntype
        self.num_etypes = num_etypes

    def forward(self, mfgs, x):
        for i in range(len(mfgs)):
            mfg = mfgs[i]
            x_dst = x[:mfg.num_dst_nodes()]
            n_src = mfg.num_src_nodes()
            n_dst = mfg.num_dst_nodes()
            mfg = dgl.block_to_graph(mfg)
            x_skip = self.skips[i](x_dst)
            for j in range(self.num_etypes):
                subg = mfg.edge_subgraph(mfg.edata['etype'] == j, preserve_nodes=True)
                x_skip += self.convs[i][j](subg, (x, x_dst)).view(-1, self.hidden_channels)
            x = self.norms[i](x_skip)
            x = F.elu(x)
            x = self.dropout(x)
        return self.mlp(x)


class ExternalNodeCollator(dgl.dataloading.NodeCollator):
    def __init__(self, g, idx, sampler, offset, feats, label):
        super().__init__(g, idx, sampler)
        self.offset = offset
        self.feats = feats
        self.label = label

    def collate(self, items):
        input_nodes, output_nodes, mfgs = super().collate(items)
        # Copy input features
        # mfgs[0].srcdata['x'] = torch.FloatTensor(self.feats[input_nodes])
        # mfgs[-1].dstdata['y'] = torch.LongTensor(self.label[output_nodes - self.offset])
        input_x = torch.FloatTensor(self.feats[input_nodes])
        target_y = torch.LongTensor(self.label[output_nodes - self.offset])
        return input_nodes, output_nodes, mfgs, input_x, target_y


def print_memory_usage():
    import os
    import psutil
    process = psutil.Process(os.getpid())
    print("memory usage is {} GB".format(process.memory_info()[0] / 1024 / 1024 / 1024))


# @profile
def train(args, dataset, g, feats, paper_offset):
    print('Loading masks and labels')
    train_idx = torch.LongTensor(dataset.get_idx_split('train')) + paper_offset
    valid_idx = torch.LongTensor(dataset.get_idx_split('valid')) + paper_offset
    label = dataset.paper_label

    print('Initializing dataloader...')
    sampler = dgl.dataloading.MultiLayerNeighborSampler([15, 25])
    train_collator = ExternalNodeCollator(g, train_idx, sampler, paper_offset, feats, label)
    valid_collator = ExternalNodeCollator(g, valid_idx, sampler, paper_offset, feats, label)
    train_dataloader = torch.utils.data.DataLoader(
        train_collator.dataset,
        batch_size=args.batch_size,
        shuffle=True,
        drop_last=False,
        collate_fn=train_collator.collate,
        num_workers=4,
        pin_memory=True # 一定要设为True
    )
    valid_dataloader = torch.utils.data.DataLoader(
        valid_collator.dataset,
        batch_size=args.batch_size,
        shuffle=True,
        drop_last=False,
        collate_fn=valid_collator.collate,
        num_workers=2,
        pin_memory=True
    )

    print('Initializing model...')
    model = RGAT(dataset.num_paper_features, dataset.num_classes, 1024, 5, 2, 4, 0.5, 'paper').cuda()
    opt = torch.optim.Adam(model.parameters(), lr=0.001)
    sched = torch.optim.lr_scheduler.StepLR(opt, step_size=25, gamma=0.25)

    best_acc = 0


    for _ in range(args.epochs):
        # 每个Epoch需要将dataloader额外包一下
        train_prefetcher = data_prefetcher(train_dataloader, dev_id=0)
        valid_prefetcher = data_prefetcher(valid_dataloader, dev_id=0)
        model.train()
        with tqdm.tqdm(train_prefetcher) as tq:
            torch.cuda.synchronize()
            t0 = time.perf_counter()
            for i, (input_nodes, output_nodes, mfgs, input_x, target_y) in enumerate(tq):
                mfgs = [g.to('cuda') for g in mfgs]

                y_hat = model(mfgs, input_x)
                loss = F.cross_entropy(y_hat, target_y)
                opt.zero_grad()
                loss.backward()
                opt.step()
                acc = (y_hat.argmax(1) == target_y).float().mean()
                tq.set_postfix({'loss': '%.4f' % loss.item(), 'acc': '%.4f' % acc.item()}, refresh=False)

        model.eval()
        correct = total = 0
        for i, (input_nodes, output_nodes, mfgs, input_x, target_y) in enumerate(tqdm.tqdm(valid_prefetcher)):
            with torch.no_grad():
                mfgs = [g.to('cuda') for g in mfgs]
                x = input_x
                y = target_y
                y_hat = model(mfgs, x)
                correct += (y_hat.argmax(1) == y).sum().item()
                total += y_hat.shape[0]
        acc = correct / total
        print('Validation accuracy:', acc)

        sched.step()

        if best_acc < acc:
            best_acc = acc
            print('Updating best model...')
            torch.save(model.state_dict(), args.model_path)


def test(args, dataset, g, feats, paper_offset):
    print('Loading masks and labels...')
    valid_idx = torch.LongTensor(dataset.get_idx_split('valid')) + paper_offset
    test_idx = torch.LongTensor(dataset.get_idx_split('test')) + paper_offset
    label = dataset.paper_label

    print('Initializing data loader...')
    sampler = dgl.dataloading.MultiLayerNeighborSampler([160, 160])
    valid_collator = ExternalNodeCollator(g, valid_idx, sampler, paper_offset, feats, label)
    valid_dataloader = torch.utils.data.DataLoader(
        valid_collator.dataset,
        batch_size=16,
        shuffle=False,
        drop_last=False,
        collate_fn=valid_collator.collate,
        num_workers=2
    )
    test_collator = ExternalNodeCollator(g, test_idx, sampler, paper_offset, feats, label)
    test_dataloader = torch.utils.data.DataLoader(
        test_collator.dataset,
        batch_size=16,
        shuffle=False,
        drop_last=False,
        collate_fn=test_collator.collate,
        num_workers=4
    )

    print('Loading model...')
    model = RGAT(dataset.num_paper_features, dataset.num_classes, 1024, 5, 2, 4, 0.5, 'paper').cuda()
    model.load_state_dict(torch.load(args.model_path))

    model.eval()
    correct = total = 0
    for i, (input_nodes, output_nodes, mfgs) in enumerate(tqdm.tqdm(valid_dataloader)):
        with torch.no_grad():
            mfgs = [g.to('cuda') for g in mfgs]
            x = mfgs[0].srcdata['x']
            y = mfgs[-1].dstdata['y']
            y_hat = model(mfgs, x)
            correct += (y_hat.argmax(1) == y).sum().item()
            total += y_hat.shape[0]
    acc = correct / total
    print('Validation accuracy:', acc)
    evaluator = MAG240MEvaluator()
    y_preds = []
    for i, (input_nodes, output_nodes, mfgs) in enumerate(tqdm.tqdm(test_dataloader)):
        with torch.no_grad():
            mfgs = [g.to('cuda') for g in mfgs]
            x = mfgs[0].srcdata['x']
            y = mfgs[-1].dstdata['y']
            y_hat = model(mfgs, x)
            y_preds.append(y_hat.argmax(1).cpu())
    evaluator.save_test_submission({'y_pred': torch.cat(y_preds)}, args.submission_path)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--rootdir', type=str, default='.', help='Directory to download the OGB dataset.')
    parser.add_argument('--graph-path', type=str, default='./graph.dgl', help='Path to the graph.')
    parser.add_argument('--full-feature-path', type=str, default='./full.npy',
                        help='Path to the features of all nodes.')
    parser.add_argument('--epochs', type=int, default=1, help='Number of epochs.')
    parser.add_argument('--batch-size', type=int, default=512)
    parser.add_argument('--model-path', type=str, default='./model.pt', help='Path to store the best model.')
    parser.add_argument('--submission-path', type=str, default='./results', help='Submission directory.')
    args = parser.parse_args()

    dataset = MAG240MDataset(root=args.rootdir)

    print('Loading graph')
    (g,), _ = dgl.load_graphs(args.graph_path)
    g = g.formats(['csc'])

    print('Loading features')
    paper_offset = dataset.num_authors + dataset.num_institutions
    num_nodes = paper_offset + dataset.num_papers
    num_features = dataset.num_paper_features
    feats = np.memmap(args.full_feature_path, mode='r', dtype='float16', shape=(num_nodes, num_features))

    if args.epochs != 0:
        train(args, dataset, g, feats, paper_offset)
    # test(args, dataset, g, feats, paper_offset)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DGL中minibatch训练子图Prefetch到GPU中加速 的相关文章

  • 2021浙江工商计算机机试1

    1不在数列中的数字 给出一个长度为n的数列 包含1到n的数字 输出1到n中不在数列中的数字 include
  • Android studio64新建APP项目时,报错 junit:junit:4.12

    大家都是要求注释掉 但不想这样 看了很多博客 快绝望的时候 用这个办法成功了 文件 E android app 工程文件目录 下面的 build gradle 文件 在这个文件中加入最后划线3行 保存即可 然后再retry apply pl
  • VS安装配置OpenCV(C++)

    目录 第一章 Opencv安装及其环境变量配置 1 1下载并安装OpenCV 1 2 OpenCV环境变量配置 第二章 Visual Studio 2019 编译器下载安装 第三章 OpenCV开发环境配置 C 3 1创建项目 3 2 添加
  • 全国信息技术标准化技术委员会汉字内码扩展规范(GBK)

    全国信息技术标准化技术委员会 汉字内码扩展规范 GBK Chinese Internal Code Specification 1 0 版 按编码顺序排列 81 丂 丄 丅 丆 丏 丒 丗 丟 丠 両 丣 並 丩 丮 丯 丱 丳 丵 丷 丼

随机推荐

  • 记一次使用EasyExcel出现Convert excel format exception.You can try specifying the ‘excelType‘ yourself

    EasyExcel 3 0 出现 com alibaba excel exception ExcelCommonException Convert excel format exception You can try specifying
  • Vue常用知识点汇总

    1 Vue常见的指令有哪些 有什么用 1 v text 会替换掉元素里的内容 2 v html 可以渲染html界面 3 v clock 防止界面闪烁 4 v bind 界面元素属性值的绑定 简写为 5 v on 事件绑定 简写为 6 v
  • K8s 管理工具 kubectl 详解

    文章目录 一 陈述式管理 1 陈述式资源管理方法 2 k8s 相关信息查看 2 1 查看版本信息 2 2 查看资源对象简写 2 3 查看集群信息 2 4 配置kubectl自动补全 2 5 查看日志 2 6 基本信息查看 2 6 1 查看m
  • SPRING是如何解决循环依赖的?为什么无法解决多例和构造器的循环依赖

    标签 java spring 文章目录 1 什么是循环依赖 2 解决循环依赖思路 3 二级缓存能否解决循环依赖 三级缓存存在的意义 4 多例和构造器为什么无法解决循环依赖 5 如何进行扩展 6 spring在创建bean的时候 在哪里创建的
  • 【C++数据结构】程序性能分析

    程序性能分析 2 1 什么是程序性能 程序性能 所谓程序性能 performance of a program 是指运行这个程序所需要的内存和时间的多少 性能分析 在性能分析 performance analysis 时 采用分析方法 性能
  • 在gitee网页中创建分支后,在vscode中更新分支

    在vscode中更新gitee上创建的分支 在网页创建分支之后 vscode中git pull origin更新在gitee创建的分支 更新之后这里有origin test分支 选中之后 通过git bracn就可以看到线上的分支了
  • 深度理解yolov3损失函数

    深度理解yolov3损失函数 在yolov3中 loss分为三个部分 一个是xywh部分带来的误差 也就是bbox带来的loss 一个是置信度带来的误差 也就是obj带来的loss 最后一个是类别带来的误差 也就是class带来的loss
  • 支付订单同步回调

    public function pay params input 接收数据 判断订单号支付方式不为空 validate this gt validate params order sn gt require pay code 支付方式 gt
  • 30秒内便能学会的30个超实用Python代码片段

    许多人在数据科学 机器学习 web开发 脚本编写和自动化等领域中都会使用Python 它是一种十分流行的语言 Python流行的部分原因在于简单易学 本文将简要介绍30个简短的 且能在30秒内掌握的代码片段 1 唯一性 以下方法可以检查给定
  • MFC多线程同步

    MFC提供了多种同步对象 下面我们只介绍最常用的四种 1 临界区 CCriticalSection 2 事件 CEvent 3 互斥量 CMutex 4 信号量 CSemaphore 一 临界区 使用CCriticalSection类 是一
  • vue路由拦截

    1 在main js中写入下面这些代码 路由拦截 router beforeEach to from next gt if to meta requireAuth 判断该路由是否需要登录权限 if sessionStorage getIte
  • git 下载和安装

    1 什么是git git 是一个分布式的版本控制工具 git 官网 git 官网 2 为什么使用git 保留之前所有版本 以便后续的修改和回滚 方便多人协同开发 3 安装git 3 1 在Linux 上安装 以 Fedora 为例 如果你在
  • Nginx Ubuntu下编译和安装

    1 登录nginx官网http nginx org en download html 下载nginx最新版本 解压nginx 得到目录nginx 1 17 8 tar zxvf nginx 1 17 8 tar gz 2 下载http fl
  • YouCompleteMe unavailable : requires Vim 7.4.143

    版本问题 ubuntu 14 05 安装完YouCompleteMe后不生效 提示 YouCompleteMe unavailable requires Vim 7 4 143经过检索与查询 ubuntu自带的vim为7 4 50 需要安装
  • js延时函数

    js延时函数总结 在js中 延迟执行函数有两种 setTimeout和setInterval 用法如下 setTimeout test 5000 5秒后执行testFunction 函数 只执行一次 setInterval test 500
  • Ubuntu小技巧9--使用Samba服务实现Windows和Linux文件访问

    Ubuntu小技巧8 Ubuntu小技巧9 使用Samba服务实现Windows和Linux文件访问 很多时候做开发的时候需要在Linux上编译运行 但是又想用Windows的优秀工具 不停的将文件拷贝到Windows和Linux是极其浪费
  • 高德地图api之编码(Geocoder)

    高德地图目前仅支持中国范围内的的地理编码和反地理编码 当我们在做搜索功能的时候 由于用户不可以记住地点的经纬度 所以只可能输入地名 所以地理编码就显得额外重要 这里我们查看一下AMap api中的地理编码 地理编码 地理编码 顾名思义就是根
  • Linux系列:编写 jar包 启动脚本

    包含指定环境 启动的脚本 指定springBoot的 application properties 创建 start sh 文件 文件内容如下 1 直接启动 bin sh 该脚本为Linux下启动java程序的 1第一个参数为方法名star
  • 设计模式-装饰模式(Decorator)

    装饰模式又叫包装模式 通过一种对客户端透明的方式来扩展对象的功能 是继承关系的一个替换方案 角色和职责 1 抽象组件 Component Car 为统一接口 也是装饰类和被装饰类的统一接口 2 具体组件 concrete Component
  • DGL中minibatch训练子图Prefetch到GPU中加速

    2022 04 update 最近DGL官方提供了prefetch的功能 可以直接使用官方实现了 https github com dmlc dgl pull 3665 导读 这两天在研究怎么加速DGL上GNN的训练 使用line prof