C медленнее, чем Java: почему? - PullRequest
10 голосов
/ 26 января 2012

Я быстро написал C программу, извлекающую i-ю строку из набора gzipped файлов (содержащих около 500 000 строк). Вот моя C программа:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <zlib.h>

/* compilation:
gcc  -o linesbyindex -Wall -O3 linesbyindex.c -lz
*/
#define MY_BUFFER_SIZE 10000000
static void extract(long int index,const char* filename)
   {
   char buffer[MY_BUFFER_SIZE];
   long int curr=1;
   gzFile in=gzopen (filename, "rb");
   if(in==NULL)
       {
       fprintf(stderr,"Cannot open \"%s\" %s.\n",filename,strerror(errno));
       exit(EXIT_FAILURE);              }
   while(gzread(in,buffer,MY_BUFFER_SIZE)!=-1 && curr<=index)
       {
       char* p=buffer;
       while(*p!=0)
           {
           if(curr==index)
               {
               fputc(*p,stdout);
               }
           if(*p=='\n')
               {
               ++curr;
               if(curr>index) break;
               }
           p++;
           }
       }
   gzclose(in);
   if(curr<index)
       {
       fprintf(stderr,"Not enough lines in %s (%ld)\n",filename,curr);
       }
   }

int main(int argc,char** argv)
   {
   int optind=2;
   char* p2;
   long int count=0;
   if(argc<3)
       {
       fprintf(stderr,"Usage: %s (count) files...\n",argv[0]);
       return EXIT_FAILURE;
       }
   count=strtol(argv[1],&p2,10);
   if(count<1 || *p2!=0)
       {
       fprintf(stderr,"bad number %s\n",argv[1]);
       return EXIT_SUCCESS;
       }
   while(optind< argc)
       {
       extract(count,argv[optind]);
       ++optind;
       }
   return EXIT_SUCCESS;
   } 

В качестве теста я написал следующий эквивалентный код в Java:

import java.io.*;
import java.util.zip.GZIPInputStream;

public class GetLineByIndex{
   private int index;

   public GetLineByIndex(int count){
       this.index=count;
   }

   private String extract(File file) throws IOException
       {
       long curr=1;
       byte buffer[]=new byte[2048];
       StringBuilder line=null;
       InputStream in=null;
       if(file.getName().toLowerCase().endsWith(".gz")){
           in= (new GZIPInputStream(new FileInputStream(file)));
       }else{
           in= (new FileInputStream(file));
       }
             int nRead=0;
       while((nRead=in.read(buffer))!=-1)
           {
           int i=0;
           while(i<nRead)
               {
               if(buffer[i]=='\n')
                   {
                   ++curr;
                   if(curr>this.index) break;
                                     }
               else if(curr==this.index)
                   {
                   if(line==null) line=new StringBuilder(500);
                   line.append((char)buffer[i]);
                   }
               i++;
               }
           if(curr>this.index) break;
           }
       in.close();
       return (line==null?null:line.toString());
       }

   public static void main(String args[]) throws Exception{
       int optind=1;
       if(args.length<2){
           System.err.println("Usage: program (count) files...\n");
           return;
       }
       GetLineByIndex app=new GetLineByIndex(Integer.parseInt(args[0]));

       while(optind < args.length)
           {
           String line=app.extract(new File(args[optind]));
           if(line==null)
               {
               System.err.println("Not enough lines in "+args[optind]);
               }
           else
               {
               System.out.println(line);
               }
           ++optind;
           }
       return;
   }
} 

Бывает, что java-программа намного быстрее (~ 1'45 '') извлекала большой индекс, чем программа C (~ 2'15 '') на той же машине (я запускал этот тест несколько раз).

Как я могу объяснить эту разницу?

Ответы [ 5 ]

22 голосов
/ 26 января 2012

Наиболее вероятное объяснение того, что версия Java быстрее, чем версия C, состоит в том, что версия C неверна.

После исправления версии C я получил следующие результаты (что противоречит вашему утверждению, что Javaбыстрее, чем C):

Java 1.7 -client: 65 milliseconds (after JVM warmed up)
Java 1.7 -server: 82 milliseconds (after JVM warmed up)
gcc -O3:          37 milliseconds

Задача состояла в том, чтобы напечатать 200000-ю строку из файла words.gz.Файл words.gz был создан с помощью gzipping /usr/share/dict/words.


...
static char buffer[MY_BUFFER_SIZE];
...
ssize_t len;
while((len=gzread(in,buffer,MY_BUFFER_SIZE)) > 0  &&  curr<=index)
    {
    char* p=buffer;
    char* endp=buffer+len;
    while(p < endp)
       {
...
15 голосов
/ 26 января 2012

Поскольку fputc () работает не очень быстро, и вы добавляете stuf char-by-char в свой выходной файл.

вызывая fputc_unlocked или, скорее, разграничивая материал, который вы хотите добавить, и вызывая fwrite ()должно быть быстрее.

12 голосов
/ 26 января 2012

Ну, ваши программы делают разные вещи.Я не профилировал вашу программу, но, глядя на ваш код, я подозреваю, что это различие:

Для построения линии вы используете это в Java:

if(curr==this.index)
{
    if(line==null) line=new StringBuilder(500);
    line.append((char)buffer[i]);
}

И это в C:

if(curr==index)
{
    fputc(*p,stdout);
}

Т.е. вы печатаете один символ за раз в стандартный вывод.По умолчанию это буфер, но я подозреваю, что он все еще медленнее, чем буфер из 500 символов, который вы используете в Java.

0 голосов
/ 26 января 2012

Очень большие буферы могут быть медленнее.Я бы посоветовал вам сделать размер буфера одинаковым.т.е. оба 2 или 8 КБ

0 голосов
/ 26 января 2012

У меня нет более глубоких знаний о том, какие оптимизации выполняет компилятор, но я думаю, в этом и заключается разница между вашими программами. Подобные микробенчмарки очень, очень, очень трудно получить правильные и значимые. Вот статья Брайана Гетца, которая развивает это: http://www.ibm.com/developerworks/java/library/j-jtp02225/index.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...