多核程序设计实验报告

实验一 Windows多线程编程

一、实验目的与要求

了解windows多线程编程机制
掌握线程同步的方法

二、实验环境和软件

Windows XP
VC 6.0

三、实验内容

1.创建线程:
HANDLE CreateThread (
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
2.线程的挂起与恢复
DWORD SuspendThread(HANDLE hThread);
DWORD ResumeThread(HANDLE hThread);
3.线程终结
BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

四、实验步骤

这个程序首先创建两个线程,当输入为1时,执行线程,否则挂起线程。
#include <windows.h>
#include
using namespace std;

DWORD WINAPI FunOne(LPVOID param){
while(true)
{
Sleep(1000);
cout<<"hello! ";
}
return 0;
}
DWORD WINAPI FunTwo(LPVOID param){
while(true)
{
Sleep(1000);
cout<<"world! ";
}
return 0;
}
int main(int argc, char* argv[])
{
int input=0;
HANDLE hand1=CreateThread (NULL, 0, FunOne, (void*)&input, CREATE_SUSPENDED, NULL);
HANDLE hand2=CreateThread (NULL, 0, FunTwo, (void*)&input, CREATE_SUSPENDED, NULL);
while(true){
cin>>input;
if(input==1)
{
ResumeThread(hand1);
ResumeThread(hand2);
}
else
{
SuspendThread(hand1);
SuspendThread(hand2);
}
};
TerminateThread(hand1,1);
TerminateThread(hand2,1);
return 0;
}:

实验结果:

实验二 蒙特卡罗法求PI

一、实验目的和要求

蒙特卡洛算法可理解为通过大量实验,模拟实际行为,来收集统计数据。本例中,算法随机产生一系列点,模拟这些点落在如下图所示的正方形区域内的情况。其几何解释如下

如图1所示,正方形边长为1,左下顶点与原点重合,两边分别与x,y轴重合。曲线为1/4圆弧,圆心位于原点,与正方形左下定点重合,半径为1。正方形面积S1=1,圆弧内面积S2= 。算法模拟大量点随机落在此正方形区域内,落在圆弧内的点的数量(n2)与点的总数(n1)的比例与面积成正比关系。即
(1)

由此可得(2)

因此,只要计算出落在圆弧内的点的数量在点总数中所占的比例,就能求出 的值。
由图1可知,所有点均落在正方形范围内,因此点的x坐标满足
。又,当点落在圆弧范围内,则点的二维坐标关系满足
。检验每一个点是否满足此关系即可判定改点是否落在圆弧内。

二、实验环境和软件

编译器:Microsoft Visual Studio C++ 6.0
操作系统:Windows XP

三、实验内容

3.1 串行算法

本项目中使用了标准C语言库中的产生随机数函数。该函数原型为:
int rand( void );
此函数产生随机数列,每次调用时均返回0到RAND_MAX之间的一个整数。
void srand( unsigned int seed );
此函数为rand()函数所生成的伪随机数序列设置起始点,使之产生不同的伪随机数。
算法:
产生2n个随机数据,范围[0,1],对每个数据点计算其坐标是否满足 ,统计满足此关系的点的数量count,则

3.2 并行算法描述

算法步骤:
1、确定需要产生的点的个数n,参与运行的处理器数m;
2、对每一个处理器,生成两个随机数x,y,范围[0,1];
3、判断两个随机数x,y是否满足

4、若满足,则变量COUNTi++;
5、重复步骤2-4,直至每个处理器均生成n/m个随机点;
6、收集COUNTi的值,并累加至变量COUNT中,此即为随机点落在圆弧内的数量;
7、通过(2)式计算 的值。

3.3 并行算法在Windows下的一个例子

#include<stdio.h>
#include<windows.h>
#include <time.h>
//#include <process.h>
#include
#include
#include<stdlib.h>
using namespace std;
HANDLE evFinish;
long cs=0; //总循环次数
long count=0; //主线程有效次数
long count_thread=0; //thread线程有效次数
time_t start, finish; //定义开始结束时间
//thread线程计算量为总数的一半
DWORD WINAPI thread(LPVOID param)
{ int i=0;
double x,y;
for(i=0;i<cs/2;i++)
{
x=(long double)rand()/(long double)RAND_MAX;
y=(long double)rand()/(long double)RAND_MAX;
if((xx+yy)<=1)
count_thread++;
//printf(“副%d “,i);
}
SetEvent(evFinish);
return 0;
}
//主线程计算量为总数的一半
int main (void)
{
evFinish=CreateEvent(NULL,FALSE,FALSE,NULL);
printf(“请输入总循环次数:”);
scanf(”%d”,&cs);
cs*=1000000;
srand( (unsigned)time( NULL ) );//用时间作随机数种子
start=time(NULL); //记录开始时间
HANDLE id=CreateThread(NULL,0,thread,NULL,0,NULL); //创建thread线程
int i=0;
double x,y;
for(i=0;i<cs/2;i++)
{
x=(long double)rand()/(long double)RAND_MAX;
y=(long double)rand()/(long double)RAND_MAX;
if((xx+yy)<=1)
count++;
// printf(“主%d”,i);
//printf(“count%d”,count);
}
WaitForSingleObject(evFinish,INFINITE);//两线程同步
count+=count_thread;
finish=time(NULL); //记录结束时间
printf(“并行情况:\n\n”);
printf(“用时=%f 秒\n”,difftime(finish,start)); //计算时间差 printf(“总共的循环次数=%d次\n”,cs);
printf(" 线程有效次数=%d次\n",count);
printf(“pi= %f \n”,4*(double)count/(double)cs);
printf(“串行行情况:\n”);
count=0;
start=time(NULL); //记录开始时间
for(i=0;i<cs;i++)
{
x=(long double)rand()/(long double)RAND_MAX;
y=(long double)rand()/(long double)RAND_MAX;
if((xx+yy)<=1)
count++;
// printf(“主%d”,i);
//printf(“count%d”,count);
}
finish=time(NULL); //记录结束时间
printf(“用时=%f 秒\n”,difftime(finish,start));
printf(“总共的循环次数=%d次\n”,cs);
printf(" 线程有效次数=%d次\n",count);
printf(“pi= %f \n”,4*(double)count/(double)cs);
return(0);
}

实验结果:

测试数据集合:由随机数函数产生的数据集合

本例中加速比(串行时间/并行时间):S=1.33

实验三 并行排序

一、实验目的与要求

在单核计算环境中,排序算法关注的核心问题是怎样减少要排序数据之间的比较次数或算法所需要的内存空间。
在多核计算环境中,每个核以线程为执行单元,排序程序可以通过生成相互协作的线程来完成排序。与单核计算环境不同的是,在多核计算环境中更关注数据集的合理划分,更致力于识别可并行执行的任务。一旦完成这些工作,程序设计上就可以生成对应的线程去执行任务。理论上,基于相同的串行算法和相同的cache命中率,多核计算速度可以无限接近单核计算速度的P倍,其中P为核的数目。
多核上的并行排序算法所面临的问题在于:

  1. 未排序的数据集合理划分到每个线程后,最后怎么汇合,形成完整的排好序的数据集呢?
  2. 怎么保证可并行执行的线程的数目和核的数目相等或稍微多于核的数目,同时也保证这些线程之间的工作量也尽可能的相同呢?
    在这个实验中,串行的算法采用标准C语言库中的快速排序函数。
    并行算法中,先将要处理的数据集均等的分到每个线程中,并使用C语言库中的快速排序函数各自排序。然后所有线程开始根据相同的数对自己的数据集进行划分,这个数是依据一定的方法选出来的(详见并行算法描述)。每个线程的数据集都会被分成K份,(其中P<= K < P2 ,P为核的数目),每份将被称为一桶。很显然这个过程选出了K个数,这些数将被成为bound_value, 记为 X1, X2, X3…… XK 。最后每个线程中小于或等于X1的数会被一个独立的线程去归并排序,同样小于或等于X2的数也会被另外一个独立的线程去归并排序,依次类推,直到排好序。
    需要指出的是:这个并行版本最消耗时间的部分是一开始每个线程各自的排序,时间为:O( );不过其数据划分和线程生成也相对简单。最后的归并排序所需时间是线性增长的,即:O( ),因此即使在最后归并部分线程执行的任务已经是不均衡的,也不会对整个程序的性能产生很大的影响。

二、实验环境和软件

编译器:Microsoft Visual Studio C++ 6.0
操作系统:Windows XP

三、实验内容

3.1 并行算法描述
算法:
将原始待排序的数据分成P等份,每个处理器上对N0个数据进行排序,称每个被排序后的子集为B0,…,Bp-1
Remain_data=N,设定第0组归并起始位置全部为0, i=0,设置第0组在目标数组中的起始位置为0
循环直至remian_data<L( L=N0/P )
3.1 选取所有子集中起始位置后续L个元素的最小值bound_value,并获得bound_value的桶号bucket
3.2 在所有子集中从起始位置到后续L个元素中选取边界位置,使得边界位置的最后一个元素小于或等于bound_value,而边界位置后的第一元素大于bound_value。
3.3 记录所有的边界位置,并设置所有第i+1组的起始位置为第i组的起始位置加上边界位置
3.4 累积所有边界值,得到该归并组的大小
3.5 根据归并组大小和本组起始位置计算第i+1组在目标数组中的起始位置。
4、设置最后一个归并组的边界为N0
5、对所有归并组进行并行P路归并排序。

四、实验步骤

说明:
A.P和多核处理器的数目相同。比如是双核的,那么P = 2;
B.Remain_data是每个线程处理的数据集中还没有被X1, X2, X3……划分过的数据集的总数目。比如,根
据X1 每个线程划分出来的数据集为x,y,z……,那么Remain_data=n – x –y –z……
并行算法在Windows下的一个例子
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <search.h>
#include<windows.h>
#include<process.h>
#ifndef _BASIC_SORT_H
#define _BASIC_SORT_H
#ifndef _SORT_P
#define _SORT_P
void* sort(void* parameter);
void generate_data(int *a,int n);
void sort_s(int *a, int n);
void view_data(int *a, int n);
int check_data_sort(int *a,int n);
int compare( const void *arg1, const void arg2 );
#define MILLION 1000000L
#define P 2
#define N0 4332539
//#define N0 1000000
#define N N0
P
#define L N0/P
void sort_p(int**d, int * b);
time_t start, finish; //定义开始结束时间
struct merge{ //归并结构
int begin[P]; //数组begin
int count[P]; //数组count
int merge_size; //归并大小
int global_pos; //全局位置
int merged; //归并是否完成
};
struct arg_merge_data{ //归并数据结构
int *d; //数组的指针
struct merge
mp; //归并结构
int *b; //整数b
int k;
};
struct arg_merge_data *tmp2;
struct forsort{
int *d;
int k;
};
struct forsort forsorta[P];
int find_bound_value(int **d,struct merge *mp,int *bucket);
int find_min(int **d,struct merge *mp,int *bucket);
void find_bound(int *d,struct merge mp,int bucket,int bound_value);
void do_last_merge_block(struct merge mp);
void merge_data(void
arg);
void view_merge(int **d,struct merge mp,int last_merge_block);
int start_time();
int diff_time();
#endif
#endif
int k; //
HANDLE Swait[P];
HANDLE Pwait[P];
HANDLE pthread[P];
HANDLE qthread[P];///
extern int d[P];
///
void generate_data(int a, int n){ //产生数据
int i;
for(i=0;i<n;i++){
a[i]=rand()%10000; //产生数组a 有n个元素
}
for(i=0;i<P;i++){
d[i]=&(a[i
N0]); //产生数组d 对应a[i]中每个n0块的第i个元素
}
}

void sort_s(int *a, int n){ //快排a[i]
qsort((void )a,n,sizeof(int),compare);
}

void sort( void *parameter){ //快排参数(数组)的前N0个元素
struct forsort *forsortb = (struct forsort *)parameter;
// printf("\nSetEvent(Swait[%d]) ",forsortb->k);/=====================
int kk=forsortb->k;
qsort(/(void)/forsortb->d, N0, sizeof(int), compare);
SetEvent(Swait[kk]);//printf("\n%d",kk);
return (void)0;
}
///
void view_data(int *a, int n){
int i=n,j;
int index=0;
while(i>N0){
for(j=0;j<N0;j++){
printf("%d\t",a[index++]); //输出a[i]中前N0个数
}
printf("\n");
i-=N0;
}
for(;i>0;i–){ //输出a[i]中N0后面的个数
printf("%d\t",a[index++]);
}
printf("\n");
}
//
int check_data_sort(int *a,int n){ //找出不服和大小顺序的位置
int i;
for(i=0;i<n-1;i++){
if(a[i]>a[i+1]) break;
}
return i;
}
//
int compare( const void *arg1, const void arg2 ){ //返回arg1与arg2的差
int a=
(int )arg1,b=(int )arg2;
return a-b;
}

int a[N];//data for parallel sort
int b[N];// the result of parallel sort
int d[P];// for parallel sort
int s[N];//data for serial sort
struct merge m_list[P
P];//record of partition
int merge_block; // the number of partition
DWORD thr_id[P
P];
long timedif;// for estimate the exection time
//struct timeval end; // …
//struct timeval start;// …

void inite()
{int i;
//forsorta=(struct forsort *) calloc(P, sizeof (struct forsort));
for(i=0;i<P;i++)
{
Swait[i]=CreateEvent(NULL,FALSE,FALSE,NULL);
Pwait[i]=CreateEvent(NULL,FALSE,FALSE,NULL);

/* for(int j=0;j<P;j++)
{
forsorta[i].d[j]=d[j];
// printf(“forsorta[%d].d[%d]=%d\n”,i,j,forsorta[i].d[j]);
}*/

}

}
void main(){
int data;
int i;
generate_data(a, N); //产生数组a[n]
for(i=0;i<N;i++) //数组s=a
s[i]=a[i];
inite();
printf(“请稍等…\n”);
//start_time();
sort_p(d,b);
//diff_time();
start_time();
sort_s(s,N);
printf(“单线程快排所用时间\n”);
diff_time();

if((data=check_data_sort(b,N))==N-1)printf("Sort is OK\n");else{printf("b[%d]>b[%d]\n",data,data+1);
}

}
//
int start_time(){ //记录开始时间函数
start=time(NULL); //记录开始时间
return 0;
}
/
int diff_time(){ //记录结束时间并算时间差函数

finish=time(NULL); //记录结束时间
printf(“用时=%f 秒 \n”,difftime(finish,start)); //输出结果
return 0;
}
///
void sort_p(int **d, int *b){

//tmp2=(arg_merge_data *) calloc(merge_block + l, sizeof (struct arg_merge_data));
int i;
int remain_data=N; //剩余数据初始化
merge_block=0;//归并块=0
start_time();
for(i=0;i<P;i++){ //m_list[0]中的两个数组初始化
m_list[0].begin[i]=0;
m_list[0].count[i]=0;
}
m_list[0].merge_size=0; //m_list[0]中的归并大小初始化
m_list[0].global_pos=0; //m_list[0]中的全局位置初始化
for(i=0; i<P; i++)
{ forsorta[i].k=i;
forsorta[i].d=d[i];
struct forsort *forsortb=&forsorta[i];
pthread[i]=CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)sort, forsortb,0,&(thr_id[i]));

}//产生p个线程 (线程id,使用sort函数,参数传递d[i])

WaitForMultipleObjects (P, Swait,TRUE,INFINITE); //等待所有线程结束/
printf(“每个线程先对自己快排时\n”);
diff_time();
start_time();
while(remain_data>LP){ //剩余数据大于LP
int bound_value;
int bucket;
bound_value=find_bound_value(d,&(m_list[merge_block]),&bucket);
find_bound(d,&(m_list[merge_block]),bucket,bound_value);
remain_data-=m_list[merge_block].merge_size;
m_list[merge_block].merged=0;
merge_block++;
if(merge_block>=P*P){ //溢出处理
printf(“block partition is greater than P^2\n”);
break;
}
}
printf(“P路线程分别找出对应的最小值并重新划分L长,直至全部划分完毕时\n”);
diff_time();

start_time();
do_last_merge_block(&(m_list[merge_block]));

//struct arg_merge_data *tmp2;
tmp2=(struct arg_merge_data *) calloc(merge_block, sizeof (struct arg_merge_data));//结构体数组分配空间
for(i=0;i<=merge_block;i++)
{ tmp2[i].k=i;
tmp2[i].d = d;
tmp2[i].mp = &(m_list[i]);
tmp2[i].b= &(b[m_list[i].global_pos]);
//pthread_create(&(thr_id[i]), NULL, &(merge_data), tmp2+i);
// k=i;
qthread[i]=CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)merge_data, tmp2+i,0,&(thr_id[i]));
}
WaitForMultipleObjects (P, Pwait,TRUE,INFINITE); ///
//pthread_join (thr_id[i], NULL);
printf(“P路线程归并完成时\n”);
diff_time();
}

int find_bound_value(int *d,struct merge mp,int bucket){ //找出本块中的追小值赋给bucket
int i;
int min;
int first=1;
for(i=0;i<P;i++){
if(mp->begin[i]+L<N0){ //本块中小于NO的部分
if(first1){
min=d[i][mp->begin[i]+L];
*bucket=i;
first=0;
}
else{
if(min>=d[i][mp->begin[i]+L]){
min=d[i][mp->begin[i]+L];
*bucket=i;
}
}
}
}
return min;
}
//
void find_bound(int **d,struct merge *mp,int bucket,int bound_value){
int i;
for(i=0;i<P;i++){
if(i!=bucket){
int tcount;
int *dp;
if(mp->begin[i]+L<N0)
tcount=L+1;
else
tcount=N0-mp->begin[i]-1;
dp=&(d[i][mp->begin[i]]);
if(tcount1){
if(dp[0]>bound_value)
tcount=0;
}
else{
if(dp[0]>bound_value){
tcount=0;
}
else if(dp[tcount]<=bound_value){
if(mp->begin[i]+L<N0)
tcount=L+1;
else
tcount=N0-mp->begin[i]-1;//maybe here has some problem
}
else{
int b=0,e=tcount;
while(!((dp[(e+b)/2]<=bound_value)&&(dp[(e+b)/2+1]>bound_value))){
if(dp[(e+b)/2]>bound_value)
e=(e+b)/2;
else
b=(e+b)/2;
}
tcount=(e+b)/2+1;
}
}
mp->count[i]=tcount;
}
else{
mp->count[i]=L+1;
}
}
mp->merge_size=0;
for(i=0;i<P;i++){
mp->merge_size+=mp->count[i];
mp[1].begin[i]=mp->begin[i]+mp->count[i];
mp[1].global_pos=mp->global_pos+mp->merge_size;
}
}
///
void do_last_merge_block(struct merge mp){
int i;
mp->merge_size=0;
for(i=0;i<P;i++){
if(mp->begin[i]>=N0)
mp->count[i]=0;
else
mp->count[i]=N0-mp->begin[i];
mp->merge_size+=mp->count[i];
}
}
//
void merge_data(void
arg){
struct arg_merge_data
tmp = (struct arg_merge_data
)arg;
struct merge temp_mp;
int
b_temp = tmp->b;
int i;
int count;
for(i=0;i<P;i++){
temp_mp.begin[i]=(tmp->mp)->begin[i];
temp_mp.count[i]=(tmp->mp)->count[i];
}
for(count=0;count<(tmp->mp)->merge_size;count++){
int bucket;
b_temp[count]=find_min((tmp->d),&temp_mp,&bucket);
temp_mp.begin[bucket]++;
temp_mp.count[bucket]–;
}
SetEvent(Pwait[tmp->k]);
}
//
int find_min(int **d,struct merge *mp,int *bucket){
int i;
int first=1;
int min_value;
for(i=0;i<P;i++){
if(mp->count[i]>0){
if(first==1){
min_value=d[i][mp->begin[i]];
*bucket=i;
first=0;
}
else{
if(min_value>d[i][mp->begin[i]]){
min_value=d[i][mp->begin[i]];
*bucket=i;
}
}
}
}
return min_value;
}
///
void view_merge(int **d,struct merge *mp,int last_merge_block){
int i;
for(i=0;i<=last_merge_block;i++){
int j;
printf(“Merge %d %d:”,i,mp[i].merge_size);
for(j=0;j<P;j++){
int k;
for(k=0;k<mp[i].count[j];k++){
printf("%d\t",d[j][mp[i].begin[j]+k]);
}
}
printf("\n");
}
printf("\n");
}

/

五、实验结果

测试数据集合:由随机数函数产生的数据集合 总共有4332539个数

本例中加速比(串行时间/并行时间):S=3.125

多核程序设计实验报告相关推荐

  1. python上机实验报告读取文件_Python程序设计实验报告:实验八 文件

    安徽工程大学 Python程序设计 实验报告 班级 物流192姓名陶俊 学号3190505235 成绩 日期 2020.6.3 指导老师修宇 实验八 文件 [实验目的] 掌握读写文本文件或 CSV 文 ...

  2. java程序设计实验报告_JavA程序设计实验报告.doc

    Java程序设计实验报告 学号: 姓名: 座位号: 实验日期: [实验名称]: JDK配置与开发工具的使用 [实验目的]: 熟悉JDK开发环境. 熟悉EditPlus编辑器或Eclipse等开发环境的 ...

  3. python上机实验报告读取文件_Python程序设计实验报告八 : 文件

    安徽工程大学 Python程序设计 实验报告 班级 物流192 姓名凌剑涛 学号 3190505233成绩 日期2020.6.3 指导老师 修宇 实验八 文件 [实验目的] 掌握读写文本文件或CSV文 ...

  4. 基于python物流管理系统毕业设计-Python程序设计实验报告一 :熟悉IDLE和在线编程平台...

    Python程序设计实验报告 班级 物流192 姓名 张羽 学号 3190505221 成绩 日期 3月5日 指导老师 修宇 实验名称 实验一 熟悉IDLE和在线编程平台 实验目的 1.掌握pytho ...

  5. 基于python物流管理系统毕业设计-Python程序设计实验报告二

    安徽工程大学 Python 程序设计 实验报告 班级: 物流192 姓名:刘晨晨 学号:3190505214 日期: 3.21 指导教师:修宇 实验二 顺序结构程序设计(验证性实验) [实验目的] ( ...

  6. 基于python物流管理系统毕业设计-Python程序设计实验报告八:文件

    安徽工程大学 Python程序设计 实验报告 .班级 物流192 姓名张羽学号3190505221成绩 日期2020.6.3指导老师修宇 实验八 文件 [实验目的] 掌握读写文本文件或 CSV 文件, ...

  7. python顺序结构实验_Python程序设计实验报告二:顺序结构程序设计

    安徽工程大学 Python程序设计 实验报告 班级 物流192 姓名方伟虎学号3190505205 成绩 日期 2020.3.20 指导老师修宇 [实验名称] 实验二 顺序结构程序设计(验证性实验) ...

  8. python顺序结构实验_Python程序设计实验报告二:顺序结构程序设计(验证性实验)...

    安徽工程大学 Python程序设计 实验报告 班级 物流191 姓名姚彩琴学号3190505129 成绩 日期 2020.3.3 指导老师修宇 [实验名称] 实验二 顺序结构程序设计(验证性实验) [ ...

  9. c语言程序设计实验报告2,C语言程序设计实验报告2.docx

    C语言程序设计实验报告2.docx 下载提示(请认真阅读)1.请仔细阅读文档,确保文档完整性,对于不预览.不比对内容而直接下载带来的问题本站不予受理. 2.下载的文档,不会出现我们的网址水印. 3.该 ...

最新文章

  1. JetBrains打造的开发神器,一套代码适应多端!
  2. Python函数之计算规则图形的面积
  3. rails3和4获取当前url
  4. mysql+sqlplus命令找不到_bash: sqlplus: command not found 解决方法
  5. mysql 的独占锁和排它锁_MySQL的排它锁与共享锁
  6. PaperNotes(16)-图神经网络GNN简史、不动点建模-笔记
  7. colorkey唇釉是否安全_好物推荐|哇哦!有被这些唇釉美到耶
  8. JAVA遍历map元素
  9. java访问权限修饰符从大到小_程序员的术与道:道——Java修饰符之非访问控制修饰符...
  10. 压缩qcow2虚拟机镜像文件
  11. Android ActivityManager
  12. 惯性导航算法matlab,基于MATLAB.Simulink的捷联惯性导航仿真
  13. Python 信号处理——短时傅里叶变换(STFT)
  14. JavaScript设计模式之状态模式
  15. mysql求分位数_给Mysql加自定义函数计算百分位数(percentile)。
  16. 我整理了50道经典Java算法题,直接进了字节跳动!!
  17. EasyCVR平台H.265视频播放加载致服务异常的偶发现象解决办法
  18. plc-st编程语言学习_这就是您可以学习所有编程语言的方式,是的-“全部”
  19. NGFF、M.2、NVME、SATA、PCIE、USB的层次和区别:协议?接口?
  20. 天龙八部元宝兑换代码 1

热门文章

  1. MPP(大规模并行处理)详解
  2. 华为丁耘,解读百尺竿头的中国5G
  3. 把CentOS7的默认yum源修改成阿里源
  4. Rancher环境搭建手册
  5. 结构体struct的自然对齐问题(经典)
  6. Yunxion资产跟踪之GNSS NEMA语句解析之GGA
  7. 微信小程序类似于点赞取消点赞计数功能
  8. 以色列突袭伊朗可能性有多大?
  9. 电子相册制作软件哪个好?快速制作婚礼请柬、旅游相册等,超简单
  10. 51红外控制电机调速