编程经验分享

笑看嫣红染半山,
逐风万里白云间,
逍遥此身不为客,
天地三才任平凡。

0%

Markdown语法中箭头的表示方法

普通箭头

箭头形状 MarkDown
$\uparrow$ $\uparrow$
$\Uparrow$ $\Uparrow$
$\downarrow$ $\downarrow$
$\Downarrow$ $\Downarrow$
$\leftarrow$ $\leftarrow$
$\Leftarrow$ $\Leftarrow$
$\rightarrow$ $\rightarrow$
$\Rightarrow$ $\Rightarrow$
$\updownarrow$ $\updownarrow$
$\Updownarrow$ $\Updownarrow$
$\leftrightarrow$ $\leftrightarrow$
$\Leftrightarrow$ $\Leftrightarrow$
阅读全文 »

理解和使用 QThread

QThread实现相关

  • Qt 4.3(包括)之前,QThread::run()纯虚函数,必须子类化QThread并实现run()函数。

    官方例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class WorkerThread : public QThread
    {
    Q_OBJECT
    void run() override {
    QString result;
    /* ... here is the expensive or blocking operation ... */
    emit resultReady(result);
    }
    signals:
    void resultReady(const QString &s);
    };

    void MyObject::startWorkInAThread()
    {
    WorkerThread *workerThread = new WorkerThread(this);
    connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
    connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
    workerThread->start();
    }

    QThread实例归属于初始化它的主线程(或者其他工作线程),而不是运行它的run()函数的新线程, 这意味着 QThread 的所有queued connections连接类型的槽函数和invoked methods 都将在旧线程(QThread实例的归属线程)执行。

    因此,希望在新线程中调用槽函数的开发人员必须使用Qt 4.4之后引入的worker-object方法; 不应将新插槽直接实现到QThread的子类中。

    queued connections连接类型的槽函数或invoked methods不同,直接调用 QThread 对象的方法将在调用该方法的线程(也就是QThread的归属线程)中执行。

    当子类化 QThread 时,请记住构造函数在旧线程中执行,而 run() 在新线程中执行。 在QThread的成员函数和在run()函数中访问QThread的成员变量时,实际上是从两个不同的线程访问该变量。 必须检查同时访问的安全性,确认是否需要加互斥锁。

    注意:

    跨线程与对象交互时必须小心。

    作为一般规则,只能从创建 QThread 对象本身的线程调用QThread的成员函数(例如 setPriority()),除非 文档另有说明。

    有关详细信息,请参阅同步线程

  • Qt 4.4开始,QThread::run() 默认调用 QThread::exec()

    这样就子类化 QThread 就不是必须的了,只需要一个继承自QObject的子类实现线程函数,并将该子类实例使用moveToThread()移动到线程对象中,并将线程函数Connect到线程对象的operator ()就可以了。

    这是 Bradley T. Hughes推荐的方法。

    阅读全文 »

从可执行文件到库

本章的主要内容有:

  • 将单个源码文件编译为可执行文件
  • 切换生成器
  • 构建和连接静态库与动态库
  • 用条件语句控制编译
  • 向用户显示选项
  • 指定编译器
  • 切换构建类型
  • 设置编译器选项
  • 为语言设定标准
  • 使用控制流进行构造

本章的示例将指导您完成构建代码所需的基本任务:编译可执行文件、编译库、根据用户输入执行构建操作等等。CMake是一个构建系统生成器,特别适合于独立平台和编译器。除非另有说明,否则所有配置都独立于操作系统,它们可以在GNU/Linux、macOS和Windows的系统下运行。

本书的示例主要为C++项目设计,并使用C++示例进行了演示,但CMake也可以用于其他语言的项目,包括C和Fortran。我们会尝试一些有意思的配置,其中包含了一些C++、C和Fortran语言示例。您可以根据自己喜好,选择性了解。有些示例是定制的,以突出在选择特定语言时需要面临的挑战。

将单个源文件编译为可执行文件

本节示例中,我们将演示如何运行CMake配置和构建一个简单的项目。该项目由单个源文件组成,用于生成可执行文件。我们将用C++讨论这个项目,您在GitHub示例库中可以找到C和Fortran的例子。

准备工作

我们希望将以下源代码编译为单个可执行文件:

1
2
3
4
5
6
7
8
#include <cstdlib>
#include <iostream>
#include <string>
std::string say_hello() { return std::string("Hello, CMake world!"); }
int main() {
std::cout << say_hello() << std::endl;
return EXIT_SUCCESS;
}

具体实施

我们把CMake指令放入一个名为CMakeLists.txt的文件中。文件的名称区分大小写,必须命名为CMakeLists.txt,CMake才能够解析。

用编辑器打开一个文本文件,将这个文件命名为CMakeLists.txt。

第一行,设置CMake所需的最低版本。如果使用的CMake版本低于该版本,则会发出致命错误:

1
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

第二行,声明了项目的名称(recipe-01)和支持的编程语言(CXX代表C++):

1
project(recipe-01 LANGUAGES CXX)

指示CMake创建一个新目标:可执行文件hello-world。这个可执行文件是通过编译和链接源文件hello-world.cpp生成的。CMake将为编译器使用默认设置,并自动选择生成工具:

1
add_executable(hello-world hello-world.cpp)

将该文件与源文件hello-world.cpp放在相同的目录中。记住,它只能被命名为CMakeLists.txt。

现在,可以通过创建build目录,在build目录下来配置项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ mkdir -p build
$ cd build
$ cmake ..
-- The CXX compiler identification is GNU 8.1.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/cmake-cookbook/chapter-01/recipe-01/cxx-example/build

如果一切顺利,项目的配置已经在build目录中生成。我们现在可以编译可执行文件:

1
2
3
4
5
$ cmake --build .
Scanning dependencies of target hello-world
[ 50%] Building CXX object CMakeFiles/hello-world.dir/hello-world.cpp.o
[100%] Linking CXX executable hello-world
[100%] Built target hello-world
阅读全文 »

GPU 硬件与 CUDA 程序开发工具


GPU 硬件

在由 CPU 和 GPU 构成的异构计算平台中,通常将起控制作用的 CPU 称为 主机(host)
将起加速作用的 GPU 称为 设备(device)

主机和设备都有自己的 DRAM,之间一般由 PCIe 总线连接。

GPU 计算能力不等价于计算性能;表征计算性能的一个重要参数是 浮点数运算峰值(FLOPS)
浮点数运算峰值有单精度和双精度之分。对于 Tesla 系列的 GPU,双精度下 FLOPS 一般是单精度下的 1/2;
对于 GeForce 系列的 GPU,双精度下 FLOPS 一般是单精度下的 1/32。

影响计算性能的另一个参数是 GPU 内存带宽(显存)


CUDA 程序开发工具

  1. CUDA;
  2. OpenCL,更为通用的各种异构平台编写并行程序的框架,AMD 的 GPU 程序开发工具;
  3. OpenACC,由多公司共同开发的异构并行编程标准。

CUDA 提供两层 API,即 CUDA 驱动API 和 CUDA 运行时API。
CUDA 开发环境中,程序应用程序是以主机(CPU)为出发点的;应用程序可以调用 CUDA 运行时 API、
CUDA 驱动 API 和一些已有的 CUDA 库。


CUDA 开发环境搭建

linux 操作系统:linux下cuda环境搭建

windows10 操作系统:windows10下cuda环境搭建


nvidia-smi 检查与设置设备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 462.30 Driver Version: 462.30 CUDA Version: 11.2 |
|-------------------------------+----------------------+----------------------+
| GPU Name TCC/WDDM | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 GeForce MX450 WDDM | 00000000:2B:00.0 Off | N/A |
| N/A 39C P8 N/A / N/A | 119MiB / 2048MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
  1. CUDA Version, 11.2;
  2. GPU Name,GeForce MX450,设备号为 0;如果系统中有多个 GPU 且只要使用其中某个特定的 GPU,
    可以通过设置环境变量 CUDA_VISIBLE_DEVICES 的值,从而可以在运行 CUDA 程序前选定 GPU;
  3. TCC/WDDM,WDDM(windows display driver model),其它包括 TCC(Tesla compute cluster);
    可以通过命令行 nvidia-smi -g GPU_ID -dm 0,设置为 WDDM 模式(1 为 TCC 模式);
  4. Compute mode, Default,此时同一个 GPU 中允许存在多个进程;其他模式包括 E.Process,
    指的是独占进程模式,但不适用 WDDM 模式下的 GPU;
    可以通过命令行 nvidia-smi -i GPU_ID -c 0,设置为 Default 模式(1 为 E.Process 模式);
  5. Perf,p8(GPU 性能状态,最大p0~最小p12);

更多关于 nvidia-smi 的资料:nvidia-smi


CUDA 中的线程组织

CUDA 虽然支持 C++ 但支持得并不充分,导致 C++ 代码中有很多 C 代码的风格。

CUDA 采用 nvcc 作为编译器,支持 C++ 代码;nvcc 在编译 CUDA 程序时,
会将纯粹的 c++ 代码交给 c++ 编译器,自己负责编译剩下的 cu 代码。


C++ 的 Hello World 程序

1
2
3
>> g++ hello.cpp -o ./bin/hello.exe
>> ./bin/hello
msvc: hello world!

CUDA 的 Hello World 程序

使用 nvcc 编译纯粹 c++ 代码

1
2
3
>> nvcc -o ./bin/hello_cu.exe hello.cu 
>> ./bin/hello_cu.exe
nvcc: hello world!

在该程序中其实并未使用 GPU。

阅读全文 »

C++11新特性

  1. C++03标准中,__cplusplus的值被预定为$199711L$,在C++11标准中,__cplusplus的值被预定为$201103L$,程序判断是否支持C++11(有些编译器是部分支持):
1
2
3
#if __cplusplus < 201103L
#error "should use C++11 implementation"
#endif
  1. 尽量用assert(n>0)进行断言,程序发布时加上宏定义值NDEBUG把断言禁掉,调试的时候去掉NDEBUG
  2. lambda函数语法定义:(参数列表和返回类型都是可选的部分,捕捉列表和函数体都可能为空。最简略的lambda函数为:[]{};
1
[capture](parameters)mutable->return-type{statement}
  • [capture]:捕捉列表,有以下几种:

    [var]表示值传递方式捕捉变量var

    [=]表示值传递方式捕捉所有父作用域的变量(包括this

    [&var]表示引用传递捕捉变量var

    [&]表示引用传递捕捉所有父作用域的变量(包括this

    [this]表示值传递方式捕捉当前的this指针

    ​ 值传递与引用传递的区别:按值方式传递的捕捉列表,其传递的值在lambda函数定义的时候就已经决定了。按引用传递的捕捉列表变量,其传递的值等于lambda函数调用时的值,举例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include<iostream>
    using namespace std;

    int main()
    {
    int j=12;
    auto by_val_lambda=[=]{return j+1;};
    auto by_ref_lambda=[&]{return j+1;};
    cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
    j++;
    cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
    }

    运行结果:

    1
    2
    3
    4
    by_val_lambda:13
    by_ref_lambda:13
    by_val_lambda:13
    by_ref_lambda:14
  • (parameters):参数列表。

    与普通函数的参数列表一致。如果不需要参数传递,则可以连同()一起省略

  • mutable:修饰符。

    默认情况下,lambda函数总是一个const函数,mutable可以取消常数特性。

    在使用该修饰符时,参数列表不可省略(即使参数为空)

  • ->return-type:返回类型。

    不需要返回值的时候可以连同符号->一起省略。

    在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。

  • {statement}:函数体。

    内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕捉的变量。

    阅读全文 »

CMake在Visual Studio下生成目录结构

通过CMAKE自带函数source_group可以实现在Visual Studio下生成目录结构。
然而如果每个项目都这样手工一个个写岂不是很累,幸好发现一个现成脚本,不用自己重复发明轮子了。

例子:

  • 目录结构
1
2
3
4
hello/include/hello.hpp
hello/src/hello.cpp
hello/CMakeLists.txt

  • CMakeLists.txt
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
cmake_minimum_required(VERSION 3.1)

project(hello)

function(assign_source_group)
foreach(_source IN ITEMS ${ARGN})
if (IS_ABSOLUTE "${_source}")
file(RELATIVE_PATH _source_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${_source}")
else()
set(_source_rel "${_source}")
endif()
get_filename_component(_source_path "${_source_rel}" PATH)
string(REPLACE "/" "\\" _source_path_msvc "${_source_path}")
source_group("${_source_path_msvc}" FILES "${_source}")
endforeach()
endfunction(assign_source_group)

function(my_add_executable)
foreach(_source IN ITEMS ${ARGN})
assign_source_group(${_source})
endforeach()
add_executable(${ARGV})
endfunction(my_add_executable)

my_add_executable(hello include/hello.hpp src/hello.cpp)

Windows10开启NTP服务器

1.进入到服务器,依次点击:windows+R弹出运行窗口—regedit—确定
然后点击确定。

img

2.依次展开:HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W32Time/TimeProviders/NtpServer
在NtpServer项的右侧键值Enablied,将默认的0
改为1,1为启用NTP服务器。

img

3.依次展开:HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W32Time/Config
找到Config项右侧的AnnounceFlags。
把默认的10改为5
5的意思就是自身为可靠的时间源

img

4.修改完以上之后关闭注册表编辑器
再:windows+R弹出运行窗口—services.msc—确定,找到Windows Time服务,启动并设置开机启动

img

img

5、打开命令提示符或PowerShell窗口‘输入net stop w32time停止服务,再输入net start w32time启动服务

img

6、输入命令w32tm /stripchart /computer:127.0.0.1,有时间回显说明配置成功了。到此在win10上配置NTP时间同步服务器的方法就介绍完了。

img

7、添加防火墙规则

控制面板—>系统和安全—>windows防火墙—>高级设置—>入站规则(右键)—>新建规则—> 端口 —下一步—-UDP—-特定本地端口:123(不要换别的)

一直点下一步,直到下面这个页面填写名称:NTP,完成

img

7.linux系统使用命令ntpdate -u $Windows-IP$同步时间就可以了,windows在时间设置上设置该机器IP然后点同步更新

步骤一

打开“终端”程序(或者iTerm2),输入如下命令:

1
user@mac % /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -kill -r -domain local -domain system -domain user

等待命令执行完成。

步骤二

按住Option键,在Dock栏上用鼠标右键点击Finder,选择“重新开启”菜单项,重启Finder

步骤三

再试试看,重复菜单项消失了。

The Raspberry Pi 4 is finally here and has a lot of exciting changes. One very major downside is that it doesn’t support true USB booting yet out of the box (like the 3 series did). The Raspberry Pi foundation states that it is being worked on and will be added back with a future update. No timeline has been given yet for that to happen but they state it’s one of their top priorities.

Most of my projects heavily depend on having good performing storage so sitting and waiting was not an acceptable solution. In this guide I’ll show you a workaround to use USB devices as your rootfs device and use a Micro SD card as bootloader only which gives us full SSD performance after boot! To see exactly how much of a performance difference this makes (spoiler: it’s gigantic) check out the Raspberry Pi Storage Benchmarks.

I highly recommend doing this on a completely new install. If you try to upgrade your old ones and something goes wrong there’s a good chance you might lose data. We will be modifying the boot partition, resizing partitions, etc. so don’t use a drive with any data on it unless you are positive you have all of the steps down!

阅读全文 »

如何在CentOS 7上使用Nginx和PHP-FPM 7.1安装Laravel 5.x

在本教程中,我将向您介绍如何在CentOS 7系统上安装带有Nginx Web服务器,PHP-FPM 7.1和MariaDB的Laravel Web Framework。 Larave …

Laravel是一个开源的PHP框架,遵循MVC(Model-View-Controller)设计模式。 Taylor Otwell于2011年创建了该项目,旨在提供CodeIgniter(CI)框架的高级替代方案。 2011年,Laravel项目发布了第1版和第2版,5.4版本已经推出了诸如Command-Line(CLI)命令行模式(artisan)的许多改进,内置了更多数据库类型的支持,并且对路由进行了改进。

本教程将介绍如何在CentOS 7系统上安装带有NginxPHP-FPM 7.1MariaDBLaravel Web Framework。 下面将逐步介绍如何在CentOS 7服务器上的LNMP下安装和配置Laravel

先决条件:

  • CentOS 7服务器。
  • root权限。
阅读全文 »