Pytorch:单卡多进程并行训练

1 导引

我们在博客《Python:多进程并行编程与进程池》中介绍了如何使用Python的multiprocessing模块进行并行编程。不过在深度学习的项目中,我们进行单机多进程编程时一般不直接使用multiprocessing模块,而是使用其替代品torch.multiprocessing模块。它支持完全相同的操作,但对其进行了扩展。

Python的multiprocessing模块可使用forkspawnforkserver三种方法来创建进程。但有一点需要注意的是,CUDA运行时不支持使用fork,我们可以使用spawnforkserver方法来创建子进程,以在子进程中使用CUDA。创建进程的方法可用multiprocessing.set_start_method(...) API来进行设置,比如下列代码就表示用spawn方法创建进程:

import torch.multiprocessing as mp
mp.set_start_method('spawn', force=True) 

事实上,torch.multiprocessing在单机多进程编程中应用广泛。尤其是在我们跑联邦学习实验时,常常需要在一张卡上并行训练多个模型。注意,Pytorch多机分布式模块torch.distributed在单机上仍然需要手动fork进程。本文关注单卡多进程模型。

2 单卡多进程编程模型

我们在上一篇文章中提到过,多进程并行编程中最关键的一点就是进程间通信。Python的multiprocessing采用共享内存进行进程间通信。在我们的单卡多进程模型中,共享内存实际上可以直接由我们的CUDA内存担任。

可能有读者会表示不对啊,Pytorch中每个张量有一个tensor.share_memory_()用于将张量的数据移动到主机的共享内存中呀,如果CUDA内存直接担任共享内存的作用,那要这个API干啥呢?实际上,tensor.share_memory_()只在CPU模式下有使用的必要,如果张量分配在了CUDA上,这个函数实际上为空操作(no-op)。此外还需要注意,我们这里的共享内存是进程间通信的概念,注意与CUDA kernel层面的共享内存相区分。

注意,Python/Pytorch多进程模块的进程函数的参数和返回值必须兼容于pickle编码,任务的执行是在单独的解释器中完成的,进行进程间通信时需要在不同的解释器之间交换数据,此时必须要进行序列化处理。在机器学习中常使用的稀疏矩阵不能序列化,如果涉及稀疏矩阵的操作会发生异常: NotImplementedErrorCannot access storage of SparseTensorImpl,在多进程编程时需要转换为稠密矩阵处理。

3 实例: 同步并行SGD算法

我们的示例采用在博客《分布式机器学习:同步并行SGD算法的实现与复杂度分析(PySpark)》中所介绍的同步并行SGD算法。计算模式采用数据并行方式,即将数据进行划分并分配到多个工作节点(Worker)上进行训练。同步SGD算法的伪代码描述如下:

注意,我们此处的多进程共享内存,是无需划分数据而各进程直接对共享内存进行异步无锁读写的(参考Hogwild!算法[3])。但是我们这里为了演示同步并行SGD算法,还是为每个进程设置本地数据集和本地权重,且每个epoch各进程进行一次全局同步,这样也便于我们扩展到同步联邦学习实验环境。

在代码实现上,我们需要先对本地数据集进行划,这里需要继承torch.utils.data.subset以自定义数据集类(参见我的博客《Pytorch:自定义Subset/Dataset类完成数据集拆分 》):

class CustomSubset(Subset):
    '''A custom subset class with customizable data transformation'''
    def __init__(self, dataset, indices, subset_transform=None):
        super().__init__(dataset, indices)
        self.subset_transform = subset_transform

    def __getitem__(self, idx):
        x, y = self.dataset[self.indices[idx]]
        if self.subset_transform:
            x = self.subset_transform(x)
        return x, y   

    def __len__(self):
        return len(self.indices)

def dataset_split(dataset, n_workers):
    n_samples = len(dataset)
    n_sample_per_workers = n_samples // n_workers
    local_datasets = []
    for w_id in range(n_workers):
        if w_id < n_workers - 1:
            local_datasets.append(CustomSubset(dataset, range(w_id * n_sample_per_workers, (w_id + 1) * n_sample_per_workers)))
        else:
            local_datasets.append(CustomSubset(dataset, range(w_id * n_sample_per_workers, n_samples)))
    return local_datasets    

local_train_datasets = dataset_split(train_dataset, n_workers) 

然后定义本地模型、全局模型和本地权重、全局权重:

local_models = [Net().to(device) for i in range(n_workers)]
global_model = Net().to(device)
local_Ws = [{key: value for key, value in local_models[i].named_parameters()} for i in range(n_workers)]
global_W = {key: value for key, value in global_model.named_parameters()}

然后由于是同步算法,我们需要初始化多进程同步屏障:

from torch.multiprocessing import Barrier
synchronizer = Barrier(n_workers)

训练算法流程(含测试部分)描述如下:

for epoch in range(epochs):
    for rank in range(n_workers):
        # pull down global model to local
        pull_down(global_W, local_Ws, n_workers)
        
        processes = []
        for rank in range(n_workers):
            p = mp.Process(target=train_epoch, args=(epoch, rank, local_models[rank], device,
                                            local_train_datasets[rank], synchronizer, kwargs))
            # We first train the model across `num_processes` processes
            p.start()
            processes.append(p)
                        
        for p in processes:
            p.join()
        
        test(global_model, device, test_dataset, kwargs)

        # init the global model
        init(global_W)
        aggregate(global_W, local_Ws, n_workers)

# Once training is complete, we can test the model
test(global_model, device, test_dataset, kwargs)

其中的pull_down()函数负责将全局模型赋给本地模型:

def pull_down(global_W, local_Ws, n_workers):
    # pull down global model to local
    for rank in range(n_workers):
        for name, value in local_Ws[rank].items():
            local_Ws[rank][name].data = global_W[name].data 

init()函数负责给全局模型进行初始化:

def init(global_W):
    # init the global model
    for name, value in global_W.items():
        global_W[name].data  = torch.zeros_like(value)

aggregate()函数负责对本地模型进行聚合(这里我们采用最简单的平均聚合方式):

def aggregate(global_W, local_Ws, n_workers):
    for rank in range(n_workers):
        for name, value in local_Ws[rank].items():
            global_W[name].data += value.data

    for name in local_Ws[rank].keys():
        global_W[name].data /= n_workers

最后,train_epochtest_epoch定义如下(注意train_epoch函数的结尾需要加上 synchronizer.wait()表示进程间同步):

def train_epoch(epoch, rank, local_model, device, dataset, synchronizer, dataloader_kwargs):
    torch.manual_seed(seed + rank)
    train_loader = torch.utils.data.DataLoader(dataset, **dataloader_kwargs)
    optimizer = optim.SGD(local_model.parameters(), lr=lr, momentum=momentum)

    local_model.train()
    pid = os.getpid()
    for batch_idx, (data, target) in enumerate(train_loader):
        optimizer.zero_grad()
        output = local_model(data.to(device))
        loss = F.nll_loss(output, target.to(device))
        loss.backward()
        optimizer.step()
        if batch_idx % log_interval == 0:
            print('{}\tTrain Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                pid, epoch + 1, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))
            
    synchronizer.wait()
    
    
def test(epoch, model, device, dataset, dataloader_kwargs):
    torch.manual_seed(seed)
    test_loader = torch.utils.data.DataLoader(dataset, **dataloader_kwargs)

    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            output = model(data.to(device))
            test_loss += F.nll_loss(output, target.to(device), reduction='sum').item() # sum up batch loss
            pred = output.max(1)[1] # get the index of the max log-probability
            correct += pred.eq(target.to(device)).sum().item()

    test_loss /= len(test_loader.dataset)
    print('\nTest Epoch: {} Global loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        epoch + 1, test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))   

我们在epochs=3n_workers=4的设置下运行结果如下图所示(我们这里仅展示每个epoch同步通信后,使用测试集对全局模型进行测试的结果):

Test Epoch: 1 Global loss: 0.0858, Accuracy: 9734/10000 (97%)
Test Epoch: 2 Global loss: 0.0723, Accuracy: 9794/10000 (98%)
Test Epoch: 3 Global loss: 0.0732, Accuracy: 9796/10000 (98%)

可以看到测试结果是趋于收敛的。
最后,完整代码我已经上传到了GitHub仓库 [Distributed-Algorithm-PySpark]
,感兴趣的童鞋可以前往查看。

参考

  • [1] Pytorch: multiprocessing
  • [2] Pytorch: What is the shared memory?
  • [3] Recht B, Re C, Wright S, et al. Hogwild!: A lock-free approach to parallelizing stochastic gradient descent[J]. Advances in neural information processing systems, 2011, 24.
数学是符号的艺术,音乐是上界的语言。
本文转载于网络 如有侵权请联系删除

相关文章

  • 一篇”水文“带你解剖HTML中的ID属性以及和Class属性的区别。

    写在开篇哈喽!我又来写”水文“了,回顾上篇讲到的class属性,那么class属性和本篇要讲的id属性有什么不一样呢?跟随笔者步伐,一一解剖。HTML中的id属性直接先上个例子,看看效果,然后我们逐一剖析一下。看下面代码:<!DOCTYPEhtml> <htmllang="zh"> <head> <metacharset="UTF-8"> <title>陪你复习巩固,攻破前端技能</title> <style> #id1{ background-color:lightblue; color:black; padding:40px; text-align:center; } #id2{ background-color:lightcoral; color:white; padding:30px; text-align:center; } </style> </head> <body> <h2id="id1"

  • 字节大量招聘芯片工程师,韦布首批5张照片全公开,京东快递北京总部被查处,今日更多大新闻在此

    日报君发自凹非寺 量子位|公众号QbitAI大家好,今天是7月13日星期三,本周工作日已经过去一半了~一起来跟日报君看看,今天科技圈发生了哪些大新闻?今日大新闻字节大量招聘芯片工程师据《科创板日报》消息,字节正招聘大量芯片相关工程师岗位,包括SoC和Core的前端设计,模型性能分析,验证,底层软件和驱动开发,低功耗设计、后端、芯片安全等。据了解,字节芯片研发团队已组建1年多,目前主攻方向分为服务器芯片、AI芯片以及视频云芯片三大类。其中,服务器芯片团队的负责人为北美高通的资深芯片人士卢山。此外,字节也从华为海思、Arm公司吸纳了不少人员。有知情人士表示,这或是为服务器Arm芯片、AI芯片的流片做准备。有接近字节的人士称,字节应该很快会拥有自己的芯片。对于字节造芯的初衷,该人士表示,一方面为了降低芯片采购的成本,另一方面,自研芯片可根据自身业务来自定义,能够大大提升性能。史上最强太空望远镜韦布首批照片发布美国国家航空航天局(NASA)公布了史上最强太空望远镜——詹姆斯·韦布拍摄的首批五张图像。它们分别是:SMACS0723星系团这个星系团SMACS0723,距离地球46亿光年,其大小相当

  • 日常pytho3练习脚本之--彩票自助选号机

    今天写个小例子难度0,主要是为了让不会python的小伙伴产生点兴趣!几行代码写个彩票自助选号机!代码非常少咱们就不上代码库了,直接贴在这里。先看效果:这里ssq2.py后边跟的参数意思为6个红球1个篮球 那么我们想要6选2呢?7+1呢? 波哥不知道大家都玩什么彩票,选多少怎么选大家可以直接组合哦!那么我们上代码:importrandom importsys #获取参数信息 r_num=int(sys.argv[1]) b_num=int(sys.argv[2]) #确定参数范围是否合理 if(r_num>5andr_num<17andb_num>0andb_num<17): #产生随机数 x=random.sample(range(1,34),r_num) #排序默认升序从小大大 x.sort() y=random.sample(range(1,17),b_num) y.sort() forredinx: print("\033[31m%s"%red,end='') print('+',end=&#x

  • 排查CPU利用率高的线程

    在日常工作中,我们有时候需要排查线上问题,找出系统中CPU利用率最高的线程.当然,我们这里默认被排查的线程在JVM中,而不是其他非JVM的线程.涉及的命令$top $ps $jstack复制思路1.根据top和ps命令查找到进程中CPU利用率最高的线程(内核级线程) 2.将内核级线程的十进制转成十六进制 3.根据jstack命令获取JVM级的线程信息复制方式一1.通过top命令找到CPU消耗(%CPU列)最高的进程,并记住PID 2.通过top-HpPID找到CPU消耗(%CPU列)最高的线程,并记住线程TID 通过printf"%x\n"十进制线程TID#将十进制转成十六进制 3.通过jstackPID|grep十六进制TID-A30复制方式二1.通过top命令找到CPU消耗(%CPU列)最高的进程,并记住PID 2.通过ps-mpPID-oTHREAD,tid,time找到CPU消耗(%CPU列)最高的线程,并记住线程TID 通过printf"%x\n"十进制线程TID#将十进制转成十六进制 3.通过jstackPID|grep十六进制TID

  • AcWing 631. Googol字符串 (递归、思维题)

    原题链接对于每个字符串,分为三个部分、前中后,中间由最独立的0组成,前面一直继承下来不变,后面记录一个反转对应的位置以及将本位上的值翻转的次数(0变1,1变0)#include<bits/stdc++.h> usingnamespacestd; #defineintlonglong intt,k,res; voiddfs(intk,intp,intcnt){ if(k==(p+1)/2){ res=0; for(inti=0;i<cnt;i++){ res^=1; } return; } if(k<(p+1)/2){ dfs(k,p/2,cnt); } else{ inttep=p/2+1-(k-p/2-1); //cout<<tep<<""<<p/2<<endl; dfs(tep,p/2,cnt+1); } } signedmain(){ cin>>t; for(int_=1;_<=t;_++){ cin>>k; intp=1; while(p<k){

  • vb添加GIF动态图片

    众说周知,GIF格式动画文件具有小巧、制作方便等特点,因此在网上得到广泛应用,在vb的picturebox和image控件添加图片后变成静止的了,这给我们设计VB应用程序带来了不便。原来以为实现起来特别的麻烦,又要注册控件,还得添加部件。通过学习方法特别很多,而且并没有多麻烦:一、最简单的方法1、在picturebox控件中添加gif格式图片首先、需要注册,注册方法:在工程中选中“引用”→引用项目ActiveMovieControltypeLibray→然后点击“确定”键即可。其次、'在PictureBox控件中显示GIF动画(图片),凡有句柄hwnd的窗体控件均可以;在窗体Form1上添加2个命令按钮Command1,Command2和1个图片框控件Picture1。(1)、在代码窗口的通用段添加:'调用运动图像类型库Quartz.dll,在c:\windows\system32目录下 PrivatepMCAsFilgraphManager'定义pMC为FilgraphManager对像 PrivatepVWAsIVideoWindow'定

  • 超越排行榜:揭示自然语言推理数据和模型中弱点的方法的调查(CS CL)

    近年来,越来越多的出版物分析自然语言推理(NLI)数据集的表面暗示,它们是否破坏了这些数据集背后的任务的复杂性,以及它们如何影响对这些数据进行优化和评估的模型。这项结构化调查通过对模型和数据集中所报告的弱点进行分类,并提出了揭示和缓解英语中那些弱点的方法,从而对正在发展的研究领域进行了概述。我们总结并讨论了发现,并针对可能的未来研究方向提出了一系列建议。我们希望这对于提议新数据集的研究人员来说,将是有用的资源,它拥有一套工具来评估其数据的适用性和质量,以评估各种令人感兴趣的现象,以及那些开发新颖的架构,进一步了解他们的改进对模型的影响的获得能力。原文标题:BeyondLeaderboards:AsurveyofmethodsforrevealingweaknessesinNaturalLanguageInferencedataandmodels原文:RecentyearshaveseenagrowingnumberofpublicationsthatanalyseNaturalLanguageInference(NLI)datasetsforsuperficialcues,whethe

  • javascript基础修炼(12)——手把手教你造一个简易的require.js

    示例代码托管在我的代码仓:http://www.github.com/dashnowords/blogs 博客园地址:《大史住在大前端》原创博文目录 华为云社区地址:【你要的前端打怪升级指南】 一.概述许多前端工程师沉浸在使用脚手架工具的快感中,认为require.js这种前端模块化的库已经过气了,的确如果只从使用场景来看,在以webpack为首的自动化打包趋势下,大部分的新代码都已经使用CommonJs或ESHarmony规范实现前端模块化,require.js的确看起来没什么用武之地。但是前端模块化的基本原理却基本都是一致的,无论是实现了模块化加载的第三方库源码,还是打包工具生成的代码中,你都可以看到类似的模块管理和加载框架,所以研究require.js的原理对于前端工程师来说几乎是不可避免的,即使你绕过了require.js,也会在后续学习webpack的打包结果时学习类似的代码。研究模块化加载逻辑对于开发者理解javascript回调的运行机制非常有帮助,同时也可以提高抽象编程能力。二.require.js2.1基本用法require.js是一个实现了AMD(不清楚AMD规范的

  • 人工智能,科技大咖眼中的未来趋势

    在人工智能,人们早在上个世纪40年代至50年代就开始了对它的研究,至今为止,人工智能也历经了多次的兴起和低谷,而今年人工智能的大爆发则归功于谷歌DeepMind团队旗下的机器人AlphaGo与韩国九段棋手李世石的那一场人机大战。在这场大战中,谷歌向人们展示了人工智能的神奇之处,也向人们展示了目前的人工智能与此前的不同,以及它的完全可实现性。虽然还有一些人恐惧人工智能所带来的变化,但是,随着人工智能带来更多的良性变化,人们更多的是怀着敬畏的心情去研发它,而人工智能时代已然成了未来的趋势和必然走向。众多大咖看好人工智能对于人工智能,有不少业内人士都发表了自己的看法,在这些言辞里,包含的不仅仅是他们的个人想法,更多的是对一种行业或技术的引导。就在本次举行的世界互联网大会上,多位大咖都纷纷发表了自己对于人工智能整体或是某一应用的看法:百度CEO李彦宏:移动互联网时代已结束,未来的机会在人工智能。在其看来,未来人工智能将在无人驾驶、自动翻译、物联网、智能物流等多个领域得到充分应用。联想CEO杨元庆:互联网正由PC互联网、移动互联网进入智能互联网的时代。他认为,智能互联网将更加深刻地改变人类的生活

  • 关于最新Struts2 S2-045漏洞修复措施的几点重要说明

    近日,安恒信息安全研究院WEBIN实验室高级安全研究员nike.zheng发现著名J2EE框架Struts2中存在远程代码执行的严重漏洞。目前Struts2官方已经确认漏洞(漏洞编号S2-045,CVE编号:CVE-2017-5638),并定级为高危风险。 该漏洞影响范围极广,影响国内外绝大多数使用Struts2开发框架的站点。受影响的软件版本为:Struts2.3.5-Struts2.3.31,Struts2.5-Struts2.5.10攻击者可通过发送恶意构造的HTTP数据包利用该漏洞,在受影响服务器上执行系统命令,进一步可完全控制该服务器,造成拒绝服务、数据泄露、网站造篡改等影响。由于该漏洞利用无需任何前置条件(如开启dmi,debug等功能)以及启用任何插件,因此漏洞危害极为严重。漏洞披露后,在业界引起轩然大波,很多厂商都发布了紧急防护方案。安恒信息研究员深入分析发现很多第三方厂商所提供的过滤方案或临时加固版本并没有完全修复漏洞,攻击者仍可以利用该漏洞入侵受影响的站点。安恒信息建议您采取以下措施有效防范Struts2S2-045漏洞:升级到Struts2.3.32或者Strut

  • 双机热备工作模式及高内聚低耦合架构解释

    双机热备份技术是一种软硬件结合的较高容错应用方案。 该方案是由两台服务器系统和一个外接共享磁盘阵列柜(也可没有,而是在各自的服务器中采取RAID卡)及相应的双机热备份软件组成:在这个容错方案中,操作系统和应用程序安装在两台服务器的本地系统盘上,整个网络系统的数据是通过磁盘阵列集中管理和数据备份的。数据集中管理是通过双机热备份系统,将所有站点的数据直接从中央存储设备读取和存储,并由专业人员进行管理,极大地保护了数据的安全性和保密性。用户的数据存放在外接共享磁盘阵列中,在一台服务器出现故障时,备机主动替代主机工作,保证网络服务不间断。双机热备份系统采用“心跳”方法保证主系统与备用系统的联系。所谓“心跳”,指的是主从系统之间相互按照一定的时间间隔发送通讯信号,表明各自系统当前的运行状态。一旦“心跳”信号表明主机系统发生故障,或者备用系统无法收到主机系统的“心跳”信号,则系统的高可用性管理软件认为主机系统发生故障,主机停止工作,并将系统资源转移到备用系统上,备用系统将替代主机发挥作用,以保证网络服务运行不间断。双机热备方案中,根据两台服务器的工作方式可以有三种不同的工作模式: 1)双机热备模式

  • Oracle 监听器无法启动(TNS-12537,TNS-12560,TNS-00507)

    Oracle启动监听报错,提示连接中断[oracle@localhost~]$lsnrctlstart LSNRCTLforLinux:Version11.2.0.1.0-Productionon06-AUG-201420:02:16 Copyright(c)1991,2009,Oracle.Allrightsreserved. Starting/opt/oracle/11g/bin/tnslsnr:pleasewait... TNS-12537:TNS:connectionclosed TNS-12560:TNS:protocoladaptererror TNS-00507:Connectionclosed LinuxError:29:Illegalseek复制这样一般是主机名不对,观察监听状态,监听主机名为localhost:[oracle@localhost~]$lsnrctlstatus LSNRCTLforLinux:Version11.2.0.1.0-Productionon06-AUG-201420:02:48 Copyright(c)1991,2009,Or

  • salesforce零基础学习(一百二十)快去迁移你的代码中的 Alert / Confirm 以及 Prompt吧

    本篇参考:https://developer.salesforce.com/blogs/2022/01/preparing-your-components-for-the-removal-of-alert-confirm-prompthttps://help.salesforce.com/s/articleView?id=release-notes.rn_lc_alert_confirm_prompt.htm&type=5&release=238https://developer.salesforce.com/docs/component-library/bundle/lightning-alert/documentationhttps://developer.salesforce.com/docs/component-library/bundle/lightning-confirm/documentation我们在项目中可能会用到alert或者confirm方法来实现一些交互性效果。 比如不满足指定的条件,我们需要alert提供一些文字来告诉用户当前数据问题,引导用户

  • 腾讯云日志服务计费概述

    日志服务(CloudLogService,CLS)支持按量计费(后付费)与资源包(预付费)两种方式,您可根据业务情况进行选择。 说明: 若已知资源使用情况需了解价格预算和计费方式选择,可前往价格计算器评估并导出价格预算清单。 计费方式 计费方式 说明 按量计费(后付费) 该方式为CLS默认计费方式,支持所有地域。根据用户的存储容量、请求、流量等计量项的具体用量进行计费,对用户账户按日进行扣费结算。 资源包(预付费) 资源包是CLS推出的优惠套餐,您可以通过资源包抵扣存储容量、请求、流量等所有按量计费项的用量。相较于按量计费方式,资源包具有较高的折扣优惠。仅适用中国站。 计费详情 类别 说明 计费周期 按量计费周期为天,即当日所产生用量的费用会在次日00:00点时开始结算。资源包为购买时一次性付费,购买完成后,资源包可用于抵扣按量计费每天的费用。 产品价格 参考日志服务产品定价。 计费项按照费用类型,日志服务包含如下计费项,其中索引流量、日志存储及索引存储费用与日志的存储类型有关,详见存储类型概述。 按照日志数据在日志服务的流转路径,其各

  • nginx:TIME_WAIT过多或者CLOSE_WAIT过多的状态

    1起因 线上服务器nginx日志运行一段时间后,会报如下错误: 1024worker_connectionsarenotenough 一般做法是修改worker_connections。但实际上:该服务是用于时间比较短的连接里,并且一天最多才4000个请求。不可能会耗尽worker_connections。除非每次连接都没有释放对应的连接。‘ shell>netstat-n|awk‘/^tcp/{++S[$NF]}END{for(ainS)printa,S[a]}’CLOSE_WAIT802ESTABLISHED106shell>lsof-n|grep“nginx对应的一个进程id”MvLogServ31125mv111uIPv4766535780t0TCP10.1.138.60:8996->10.1.138.60:51977(CLOSE_WAIT)MvLogServ31125mv112uIPv4766596980t0TCP10.1.138.60:8996->10.1.138.60:52015(CLOSE_WAIT)MvLogServ31125mv113u

  • 怎样查看Eclipse是32位还是64位

     方法一: 1、通过配置文件查看,找到eclipse的安装路径 2、点击打开【打开文件位置】,进入到eclipse的安装目录,找到eclipse.ini文件 3、找到配置文件后,使用文本编辑工具,或记事本打开,可以看到eclipse的一些配置信息,如下图 4、如果是win32.x86,则是32位的Eclipse,如果是“win32.x86_64”,则是64位的Eclipse,如下图:   方法二: 1、打开eclipse软件,具体依次点击【Help】-->【AboutEclipse】,可见如下图   2、再点击【InstallationDetails】-->【Configuration】可以看到相应eclipse信息,   同样可见如果是win32.x86,则是32位的Eclipse,如果是“win32.x86_64”,则是64位的Eclipse。  

  • Mongodb中 Documents文档说明

    mongodb使用BSON格式存储数据记录.如下图: 文档结构 文档有键值对组成,有以下结构: {    field1:value1,    field2:value2,    ...    fieldN:valueN}​ 字段的值可以是任意BSON数据类型,包括其他文档,数组和文档数组. 例如,以下文档包含不同类型的值: {   _id:ObjectId("5099803df3f4948bd2f98391"),    name:{first:"Alan",last:"Turing"},    birth:newDate('Jun23,1912'),    death:newDate('Jun07,1954'),    cont

  • python学习手册笔记——38.装饰器

    00.装饰是未函数和类指定管理代码的一种方式。装饰器本身的形式是处理其他的可调用对象的可调用的对象。*函数装饰器在函数定义的时候进行名称重绑定,提供一个逻辑层来管理函数和方法或随后对它们的调用。*类装饰器在类定义的时候进行名称重绑定,提供一个逻辑层来管理,或管理随后调用它们所创建的示例。 01.管理调用和实例*函数装饰器安装包装器对象,以在需要的时候拦截随后的函数调用并处理它们*类装饰器安装包装器对象,以在需要的时候拦截随后的实例创建调用并处理它们。 02.装饰器未这样的任务提供了一种显式的语法,它使得意图明确,可以最小化扩展代码的冗余,并且有助于确保正确的API使用。*装饰器由一种非常明确的语法*当主体函数或类定义的时候,装饰器应用一次 03.函数装饰器:函数装饰器是一种关于函数的运行时声明,函数的定义需要遵守此声明。 04.装饰器func(6,7)decorator(func)(6,7) 05.classC:@staticmethoddefmeth(...):...classC:@propertydefname(self):.... 05.defdecorator(F):#Save

  • Qt 绘图进阶之二:双缓冲机制与实例

    一、双缓冲机制 所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性地绘制到控件上。 在早期的Qt版本中,若直接在控件上进行绘制工作,则在控件重绘时会产生闪烁的现象,控件重绘频繁时,闪烁尤为明显。双缓冲机制可以有效地消除这种闪烁现象。自Qt5版本之后,QWidget控件已经能够自动处理闪烁的问题。 因此,在控件上直接绘图时,不用再操心显示的闪烁问题,但双缓冲机制在很多场合仍然有其用武之地。当所需绘制的内容较复杂并需要频繁刷新,或者每次只需要刷新整个控件的一小部分时,仍应尽量采用双缓冲机制。 二、实例效果图 实现一个简单的绘图工具,可以选择线形,线宽,颜色等基本要素。效果图如下所示: 三、实例核心代码 PaintArea.h: #ifndefPAINTAREA_H #definePAINTAREA_H #include<QWidget> #include<QPen> #include<QBrush> #include<QMouseEvent> #include<QPainter> //绘图

  • Easyui的datagrid中的if判断问题

    初学easyui,在columns里面有这么一列 { field:'alert', title:'报警类型', width:150, rowspan:2, align:'center' } 复制 想判断当datagrid里面的值为"1"(String类型)的时候列表显示为A,值为"2"的时候显示B,参考下面 { field:'alert', title:'报警类型', width:150, rowspan:2, align:'center', formatter:function(value,row,index){ if(value=='1'){return'A'} elseif(value=='2'){ return'B' } } } 复制

  • Python编写从ZabbixAPI获取信息

    此脚本用Python3.6执行是OK的。 #-*-coding:utf-8-*-importjsonimporturllib.request,urllib.error,urllib.parseclassZabbixAPI:def__init__(self):self.__url='http://192.168.56.102/zabbix/api_jsonrpc.php'self.__user='admin'self.__password='zabbix'self.__header={"Content-Type":"application/json-rpc"}self.__token_id=self.UserLogin()#登陆获取tokendefUserLogin(self):data={"jsonrpc":"2.0","method":"user.login","params":{"user":self.__user,"password":self.__password},"id":0,}returnself.PostRequest(data)#推送请求defPostRequest(

相关推荐

推荐阅读