[C++]Linux之文件拷貝在系統調用和C庫函數下的效率比較


 聲明:如需引用或者摘抄本博文源碼或者其文章的,請在顯著處注明,來源於本博文/作者,以示尊重勞動成果,助力開源精神。也歡迎大家一起探討,交流,以共同進步~ 0.0

 

題目:

  1. 分別利用文件的系統調用read、write和文件的庫函數fread、fwrite實現文件復制功能,比較在每次讀取一個字節和1024字節時兩個程序的執行效率,並分析原因。

 

分析:

   預先准備好一份已經存儲數據的普通文件(data.txt)

  設置兩對照組:

    對照組1(系統調用組):在執行系統調用實現文件拷貝功能時,分別對讀取一個字節和1024個字節記時,讀取的執行時間若分別記為t1和t1024,然后通過比較1024×t1與t1024二者的時間來比較執行效率(時間效率為主)。

    對照組2(庫函數組):在執行庫函數實現文件拷貝功能時,分別對文件的系統調用與文件的庫函數的執行時間形成對照,比較二者執行效率。

    即 本次實驗需要記錄4個變量:

      1)系統調用的讀取1字節的時間:Syst1

      2)系統調用的讀取1024字節的時間:Syst1024

      3)庫函數的讀取1字節的時間:Lib1

      4)庫函數的讀取1024字節:Lib1024

  猜想:

    系統調用的性能較庫函數高,所以,性能上:Syst > Lib;

    讀取字節數小的時間效率將高於讀取字節數大的時間效率,所以,時間上:T(1) < T(1024)

    則,猜想結論: Lib1024 > Syst1024 > Lib1 > Syst1 (耗時)

  開始實施實驗,驗證猜想 Action!

實驗

 【系統調用組】
gcc main-system.c -o main-system.out
time ./main-system.out 
 /*
      @author:johnny
      @description:系統調用
  */

#include<stdio.h>
#include<stdlib.h>//exit(num)
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<time.h>
#include<unistd.h>//dependency:close/write/open method

#define LENGTH_1    1
#define LENGTH_1024 1024

//統計系統調用的讀取1字節的時間效率,並實現文件復制功能
double read1Byte(){
	double startTime;
	double endTime;
	double Syst1;//系統調用的讀取1字節的時間:Syst1
	
	int len,fread,fwrite;
	off_t off;//記錄文件游標偏移位置
	char readStr[LENGTH_1];
	fread = open("data.txt",O_RDWR | O_CREAT,S_IRUSR | S_IWUSR);//打開數據源文件
	fwrite = creat("copy-1-data.txt", 0700);//創建拷貝文件
	if(fwrite == -1){
		perror("\n[read1Byte] Fail to create file 'copy-1-data.txt'.\n\n");
		exit(1);
	} else {
		printf("\n[read1Byte] Create file 'copy-1-data.txt' OK.\n\n");	
	}
	if(fwrite){//write data to file.
		off = lseek(fwrite,0,SEEK_SET);	//獲取並重置游標位置為文件開頭處,SEEK_CUR當前讀寫位置后增加offset[0]個位移量
		if(off == -1){
			perror("\n[read1Byte] Fail to lseek.\n\n");	
			exit(1);	
		}		
		startTime = clock();//從打開文件后,讀取文件數據前開始記錄時間		
		len = read(fread, readStr, LENGTH_1);//從data.txt讀取數據
		endTime = clock();
		while(len){//判斷len是否為0,如果為0,則說明讀取到了文件尾,則停止
			printf("%s", readStr);
			write(fwrite, readStr,strlen(readStr));	//一邊讀取源數據文件,一邊寫入新文件數據
			len = read(fread, readStr, LENGTH_1);
		}
	} 

	close(fread);
	close(fwrite);

	Syst1 = (double)(endTime - startTime) / 1000; //(單位:毫秒)
	printf("\n[read1Byte] 系統調用的讀取1字節的時間[Syst1]:%f ms\n\n", Syst1);

	return Syst1;
}

//統計系統調用的讀取1024字節的時間效率,並實現文件復制功能
double read1024Bytes(){
	double startTime;
	double endTime;
	double Syst1024;//系統調用的讀取1024字節的時間:Syst1024
	
	int len,fread,fwrite;
	off_t off;//記錄文件游標偏移位置
	char readStr[LENGTH_1024];
	fread = open("data.txt",O_RDWR | O_CREAT,S_IRUSR | S_IWUSR);//打開數據源文件
	fwrite = creat("copy-1024-data.txt", 0700);//創建拷貝文件
	if(fwrite == -1){
		perror("\n[read1024Byte] Fail to create file 'copy-1024-data.txt'.\n\n");
		exit(1);
	} else {
		printf("\n[read1024Bytes] Create file 'copy-1024-data.txt' OK.\n\n");	
	}
	if(fwrite){//write data to file.
		off = lseek(fwrite,0,SEEK_SET);	//獲取並重置游標位置為文件開頭處,SEEK_CUR當前讀寫位置后增加offset[0]個位移量
		if(off == -1){
			perror("\n[read1024Bytes] Fail to lseek.\n");	
			exit(1);	
		}		
		startTime = clock();//從打開文件后,讀取文件數據前開始記錄時間		
		len = read(fread, readStr, LENGTH_1024);//從data.txt讀取數據
		endTime = clock();
		while(len){//判斷len是否為0,如果為0,則說明讀取到了文件尾,則停止
			printf("%s", readStr);
			write(fwrite, readStr,strlen(readStr));	//一邊讀取源數據文件,一邊寫入新文件數據
			len = read(fread, readStr, LENGTH_1024);
		}
	} 

	close(fread);
	close(fwrite);

	Syst1024 = (double)(endTime - startTime) / 1000; //(單位:毫秒)
	printf("\n[read1024Bytes] 系統調用的讀取1024字節的時間[Syst1024]:%f ms\n\n", Syst1024);

	return Syst1024;	
}

int main(){
	read1Byte();//統計系統調用的讀取1字節的時間效率,並實現文件復制功能
	read1024Bytes();//統計系統調用的讀取1024字節的時間效率,並實現文件復制功能
	return 0;
}

 運行效果:

 

【庫函數調用組】
gcc main-lib.c -o main-lib.out
time ./main-lib.out
/*

 @author:johnny
 @description:庫函數
*/

#include<stdio.h>
#include<stdlib.h>//exit(num)
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<time.h>
#include<unistd.h>//dependency:close/write/open method

#define LENGTH_1    1
#define LENGTH_1024 1024

//統計庫函數的讀取1字節的時間效率,並實現文件復制功能
double read1Byte(){
	double startTime;
	double endTime;
	double Lib1;//庫函數的讀取1字節的時間:Lib1
	
	FILE * readStream;//讀取數據源文件data.txt的文件流
	FILE * writeStream;//拷貝數據源文件data.txt的文件流
	readStream = fopen("data.txt","r+");//只讀方式打開可讀寫的且必須已經存在的文件
	writeStream = fopen("copy-1-data.txt","w+");//只寫方式打開文件。若文件存在則文件長度清為零,即該文件內容會消失。若文件不存在則建立該文件。
	if(readStream == NULL){
		fprintf(stderr,"\n[read1Byte] readStream can not open file 'data.txt'.\n\n");
		return 0;	
	}
	if(writeStream == NULL){
		fprintf(stderr,"\n[read1Byte] writeStream can not create/open file 'copy-1-data.txt'.\n\n");
		return 0;	
	} else {
		printf("\n[read1024Bytes] writeStream create/open file 'copy-1-data.txt' OK.\n\n");	
	}
	
	size_t len;//記錄每次實際讀取到的緩沖數據長度,若為0,則可能是讀取錯誤或者已讀到文件尾。 
	int off;//記錄文件游標當前偏移位置

	char buffer[LENGTH_1];
	//write data to file.
	off = fseek(writeStream,0,SEEK_SET);//獲取並重置游標位置為文件開頭處,SEEK_CUR當前讀寫位置后增加offset[0]個位移量
	if(off == -1){
		perror("\n[read1Byte] Fail to fseek.\n\n");	
		exit(1);	
	}		
	startTime = clock();//從打開文件后,讀取文件數據前開始記錄時間		
	len = fread(buffer, LENGTH_1, LENGTH_1, readStream);//從data.txt讀取數據,len為返回實際寫入的nmemb數目
	endTime = clock();
	while(len){//判斷len是否為0,如果為0,則說明讀取到了文件尾,則停止
		printf("%s", buffer);
		fwrite(fwrite, LENGTH_1, len, writeStream);	//一邊讀取源數據文件,一邊寫入新文件數據
		len = fread(buffer, LENGTH_1, LENGTH_1, readStream);
	} 

	fclose(readStream);	
	fclose(writeStream);		

	Lib1 = (double)(endTime - startTime);
	printf("\n[read1Byte] 庫函數的讀取1字節的時間[Lib1]:%f ms\n\n", Lib1);

	return Lib1;
}

//統計庫函數的讀取1024字節的時間效率,並實現文件復制功能
double read1024Bytes(){
	double startTime;
	double endTime;
	double Lib1024;//庫函數的讀取1024字節的時間:Lib1024
	
	FILE * readStream;//讀取數據源文件data.txt的文件流
	FILE * writeStream;//拷貝數據源文件data.txt的文件流
	readStream = fopen("data.txt","r+");//只讀方式打開可讀寫的且必須已經存在的文件
	writeStream = fopen("copy-1024-data.txt","w+");//只寫方式打開文件。若文件存在則文件長度清為零,即該文件內容會消失。若文件不存在則建立該文件。
	if(readStream == NULL){
		fprintf(stderr,"\n[read1024Bytes] readStream can not open file 'data.txt'.\n\n");
		return 0;	
	}
	if(writeStream == NULL){
		fprintf(stderr,"\n[read1024Bytes] writeStream can not create/open file 'copy-1024-data.txt'.\n\n");
		return 0;	
	} else {
		printf("\n[read1024Bytes] writeStream create/open file 'copy-1024-data.txt' OK.\n\n");	
	}
	
	size_t len;//記錄每次實際讀取到的緩沖數據長度,若為0,則可能是讀取錯誤或者已讀到文件尾。 
	int off;//記錄文件游標當前偏移位置

	char buffer[LENGTH_1024];
	//write data to file.
	off = fseek(writeStream, 0, SEEK_SET);//獲取並重置游標位置為文件開頭處,SEEK_CUR當前讀寫位置后增加offset[0]個位移量
	if(off == -1){
		perror("\n[read1024Bytes] Fail to fseek.\n\n");	
		exit(1);	
	}		
	startTime = clock();//從打開文件后,讀取文件數據前開始記錄時間		
	len = fread(buffer, LENGTH_1, LENGTH_1024, readStream);//從data.txt讀取數據,len為返回實際寫入的nmemb數目
	endTime = clock();
	while(len){//判斷len是否為0,如果為0,則說明讀取到了文件尾,則停止
		printf("%s", buffer);
		fwrite(fwrite, LENGTH_1, len, writeStream);	//一邊讀取源數據文件,一邊寫入新文件數據
		len = fread(buffer, LENGTH_1, LENGTH_1024, readStream);
	}

	fclose(readStream);	
	fclose(writeStream);		

	Lib1024 = (double)(endTime - startTime);
	printf("\n[read1024Bytes] 庫函數的讀取1字節的時間[Lib1024]:%f ms\n\n", Lib1024);

	return Lib1024;	
}

int main(){
	read1Byte();//統計庫函數的讀取1字節的時間效率,並實現文件復制功能
	read1024Bytes();//統計庫函數的讀取1024字節的時間效率,並實現文件復制功能
	return 0;
}

 運行效果:

 

結果:

 

變量 讀取字節數(byte) API 消耗時間(ms)

Syst1

 

1

 系統調用  0.061
 Syst1024  1024  系統調用  0.002
 Lib1  1  庫函數  46
 Lib024  1024  庫函數 14

 

結論:

  實驗后,回顧最初的猜想:

    系統調用的性能較庫函數高,所以,性能上:Syst > Lib;

    讀取字節數小的時間效率將高於讀取字節數大的時間效率,所以,時間上: T(1) < T(1024)

    那么,猜想結論:

      Lib1024 > Syst1024 > Lib1 > Syst1 (耗時)

   然而,實際實驗結論是:

      Lib1 > Lib1024 >> Syst1 > Syst1024(耗時)

    發現,猜想僅僅正確一半。

  字節數小的時間效率反而遠遠高於讀取字節數大的時間效率,且系統調用的效率遠遠高於之前對系統調用與庫函數調用的想象。

  實驗數據顯示,分別比較讀取1字節和讀取1024字節在系統調用組,庫函數組的時間效率:

Lib1 ~= 754.098 * Syst1 (時間效率比較:754.098倍)

Lib1024 = 7000 * Syst1024 (時間效率比較:7000倍)

  對於系統調用與庫函數之間的效率差距,在查閱相關文獻后,得知大致原因如下:

    要實現文件拷貝,必須嵌入內核,從磁盤讀取文件內容,然后存儲到另一個文件。

    實現文件拷貝最通常的做法是:

      讀取文件用系統調用read()函數,讀取到一定長度的連續的用戶層緩沖區,然后使用write()函數將緩沖區內容寫入文件。也可以用標准庫函數fread()和fwrite(),但這兩個函數最終還是通過系統調用read()和write()實現拷貝的,因此可以歸為一類(不過效率肯定沒有直接進行系統調用的高)。一個更高級的做法是使用虛擬存儲映射技術進行,這種方法將源文件以共享方式映射到虛擬存儲器中,目的文件也以共享方式映射到虛擬地址空間中,然后使用memcpy高效地將源文件內容復制到目的文件中。

  實驗數據顯示,分別比較系統調用組,庫函數組在讀取1字節和讀取1024字節的時間效率:

Syst1 = 30.5 * Syst1024 (時間效率比較:30.5倍)

Lib1 ~= 3.286 * Lib1024 (時間效率比較:約3.3倍)

  對於單(低)字節與多(更多)字節之間的時間效率差距,查閱相關資料無效后,個人揣測原因如下:(仍存在疑問)

    本來系統讀取數據的緩存區是較大的,可以讀取多個字節數據,這樣能夠解釋為何多(更多)字節數據的讀取高的原因。又由於讀取單(低)字節,需要系統額外地設置和讀取緩存區,導致比讀取更多字節更高的時間開銷,所以單(低)字節的效率便低於多字節的時間效率了。

 

參考文獻:

  [原創]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM