2014-11-20 5 views
-2

아래 코드는 곱셈을 수행하는 데 매우 비효율적 인 알고리즘입니다. 그것은 시험 목적으로 쓰여졌다. 나는 다른 언어로 같은 코드를 썼다고 믿는다.Java는 C보다 2 배 이상 빠릅니다 (C++의 하위 집합)

바로 아래는 코드를 실행 한 결과입니다.

OS: Windows 7 
language: C (as a subset of C++) 

compiler: Visual C++ 
optimization option: /Ox /Oi /Ot /Oy /GL 
running time (seconds): 40 +/- 1 

compiler: MinGW/gcc 
optimization option: -O3 march=native 
running time (seconds): 81 +/- 1 

compiler: MinGW/g++ 
optimization option: -O3 march=native 
running time (seconds): 82 +/- 1 

language: Java 

compiler: Oracle JDK 
VM: Oracle JVM 
running time (seconds): 18 +/- 1 

나는 완전한 최적화를 갖춘 컴파일러가 최적화를 할 수 없다는 것을 내 C 코드에서 끔찍한 짓을 저질렀다고 생각한다. 큰 문제가 있으면 알려주세요. 나는 많은 양의 계산을 다루는 부분을 가진 프로젝트를 계획 중이다. C에서이 핵심 계산 부분을 작성하기로 결정했으나 이런 종류의 결과로 모든 것을 Java로 작성할 수 있습니다. 훨씬 더 쉽고 빠릅니다. 나는 아직도 C를 믿는다. 그래서 내 코드에 문제가 있다면 알려줘. 내가 기대하는 것은 자바 버전이 1.5 배 이상 느리게해야한다고했지만, 어떻게 든 C.

TEST.C

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <time.h> 

typedef signed char byte; 

typedef struct _Array 
{ 
    byte *data; 
    int len; 
} 
Array; 

void new_Array(Array *a, int len) 
{ 
    a->data = (byte *)malloc(len * sizeof(byte)); 
    a->len = len; 
} 

void del_Array(Array *a) 
{ 
    free(a->data); 
} 

typedef struct _BUI 
{ 
    Array num; 
    int len; 
} 
BUI[1]; 

void new_BUI(BUI b, const char *s) 
{ 
    int len = strlen(s); 
    b->len = len; 
    new_Array(&b->num, len); 
    for (int i = 0; i < len; ++i) 
    { 
     b->num.data[i] = s[len - i - 1] - '0'; 
    } 
} 

void del_BUI(BUI b) 
{ 
    del_Array(&b->num); 
} 

int BUI_cmp(const BUI a, const BUI b) 
{ 
    if (a->len > b->len) 
    { 
     return 1; 
    } 
    if (a->len < b->len) 
    { 
     return -1; 
    } 
    for (int i = a->len - 1; i >= 0; --i) 
    { 
     if (a->num.data[i] > b->num.data[i]) 
     { 
      return 1; 
     } 
     if (a->num.data[i] < b->num.data[i]) 
     { 
      return -1; 
     } 
    } 
    return 0; 
} 

#define MAX(A, B) (A > B ? A : B) 

void BUI_add(BUI r, const BUI a, const BUI b) 
{ 
    Array c; 
    new_Array(&c, MAX(a->len, b->len) + 1); 
    memset(c.data, 0, c.len); 
    memcpy(c.data, a->num.data, a->len); 
    for (int i = 0; i < b->len; ++i) 
    { 
     c.data[i] += b->num.data[i]; 
    } 
    for (int i = 0; i < c.len - 1; ++i) 
    { 
     if (c.data[i] >= 10) 
     { 
      c.data[i + 1] += c.data[i]/10; 
      c.data[i] %= 10; 
     } 
    } 
    del_Array(&r->num); 
    r->num = c; 
    r->len = c.len; 
    for (int i = r->num.len - 1; r->num.data[i--] == 0; --r->len); 
} 

void BUI_mul(BUI r, const BUI a, const BUI b) 
{ 
    BUI c; 
    new_BUI(c, "0"); 
    { 
     BUI one; 
     new_BUI(one, "1"); 
     BUI i; 
     new_BUI(i, "0"); 
     for (; BUI_cmp(i, a) < 0; BUI_add(i, i, one)) 
     { 
      BUI_add(c, c, b); 
     } 
     del_BUI(one); 
     del_BUI(i); 
    } 
    del_Array(&r->num); 
    r->num = c->num; 
    r->len = c->len; 
} 

void BUI_print(BUI b) 
{ 
    for (int i = b->len - 1; i >= 0; --i) 
    { 
     putchar(b->num.data[i] + '0'); 
    } 
} 

int main(void) 
{ 
    BUI a; 
    new_BUI(a, "123456789"); 
    BUI b; 
    new_BUI(b, "987654321"); 
    BUI_print(a); 
    fputs(" x ", stdout); 
    BUI_print(b); 
    fputs(" = ", stdout); 
    time_t start_time = clock(); 
    BUI_mul(a, a, b); 
    time_t end_time = clock(); 
    BUI_print(a); 
    del_BUI(a); 
    del_BUI(b); 
    printf("\nelapsed time: %.3f\n", (double)(end_time - start_time)/CLOCKS_PER_SEC); 
    printf("%d %d\n", a->num.len, a->len); 
    return 0; 
} 

Test.java

import java.util.*; 

class BUI 
{ 
    byte[] num; 
    int len; 

    BUI(String s) 
    { 
     len = s.length(); 
     num = new byte[len]; 
     for (int i = 0; i < len; ++i) 
     { 
      num[i] = (byte)Character.getNumericValue(s.charAt(len - i - 1)); 
     } 
    } 

    int cmp(BUI b) 
    { 
     if (len > b.len) 
     { 
      return 1; 
     } 
     if (len < b.len) 
     { 
      return -1; 
     } 
     for (int i = len - 1; i >= 0; --i) 
     { 
      if (num[i] > b.num[i]) 
      { 
       return 1; 
      } 
      if (num[i] < b.num[i]) 
      { 
       return -1; 
      } 
     } 
     return 0; 
    } 

    void add(BUI a, BUI b) 
    { 
     byte[] c = new byte[Math.max(a.len, b.len) + 1]; 
     Arrays.fill(c, (byte)0); 
     System.arraycopy(a.num, 0, c, 0, a.num.length); 
     for (int i = 0; i < b.len; ++i) 
     { 
      c[i] += b.num[i]; 
     } 
     for (int i = 0; i < c.length - 1; ++i) 
     { 
      if (c[i] >= 10) 
      { 
       c[i + 1] += c[i]/10; 
       c[i] %= 10; 
      } 
     } 
     num = c; 
     len = c.length; 
     for (int i = num.length - 1; num[i--] == 0; --len); 
    } 

    void mul(BUI a, BUI b) 
    { 
     BUI c = new BUI("0"); 
     { 
      BUI one = new BUI("1"); 
      BUI i = new BUI("0"); 
      for (; i.cmp(a) < 0; i.add(i, one)) 
      { 
       c.add(c, b); 
      } 
     } 
     num = c.num; 
     len = c.len; 
    } 

    void print() 
    { 
     for (int i = len - 1; i >= 0; --i) 
     { 
      System.out.print(num[i]); 
     } 
    } 
} 


public class Test 
{ 
    public static void main(String[] args) 
    { 
     BUI a = new BUI("123456789"); 
     BUI b = new BUI("987654321"); 
     a.print(); 
     System.out.print(" x "); 
     b.print(); 
     System.out.print(" = "); 
     long start_time = System.currentTimeMillis(); 
     a.mul(a, b); 
     long end_time = System.currentTimeMillis(); 
     a.print(); 
     System.out.printf("\nelapsed time: %.3f\n", (end_time - start_time)/1000.0); 
    } 
} 
+0

설명은 확장 토론이 아닙니다. 이 대화는 [채팅으로 이동되었습니다] (http://chat.stackoverflow.com/rooms/65295/discussion-on-question-by-xiver77-java-is-2-times-faster-than-c-as- a-subset-of). –

답변

8
을 능가하는 성능

"language : C (C++의 하위 집합)".

그냥 아니요입니다.

C는 C++의 하위 집합이 아닙니다. 그들은 공통 문법 (대부분 C 언어)을 가지고 있지만 대부분의 런타임 검사는 다르다. (컴파일러에 의존한다.) 코드 해석 방식이 다르다.

C++에는 알고리즘 구현 속도가 빨라지는 몇 가지 기능이 있습니다 (정확히 얼마인지는 모르겠지만 올바르게 측정하면 변경 사항을 분명히 볼 수 있습니다).

간단한 예를 들어, 문자 배열에 std :: string을 사용하십시오.

많은 양의 계산을 처리하는 프로젝트를 계획 중입니다. C에서이 핵심 계산 부분을 작성하기로 결정했으나 이런 종류의 결과로 모든 것을 Java로 작성할 수 있습니다.

이동 (즉, 더 간단하면 Java로 작성)하십시오. Java에서 더 나은 성능을 얻을 수있는 것처럼 C로 더 나은 성능을 얻을 수 있습니다. 알고리즘과 코드를 최적화하는 데 드는 시간과 노력을 결정하는 것은 당신에게 달려 있습니다.

최적화 옵션을 사용하여 컴파일러를 실행하면 계산 속도가 향상되지 않습니다. 실제로는 그렇지만 알고리즘 최적화로 인한 속도에 비해 상대적으로 작습니다. 익숙한 개발 도구 체인을 최적화 한 다음 익숙하지 않은 언어를 걸러서 속도를 높일 수 있습니다.

더욱 쉽고 빠릅니다. 나는 아직도 C를 믿는다. 그래서 내 코드에 문제가 있다면 알려줘. 내가 예상 한 바는 Java 버전이 1.5 배 이상 느려야한다는 것이었지만 C를 능가하는 성능을 보였다.

논리에 결함이 있습니다.

작성한 비교는 어떤 식으로도 C와 Java (그리고 C++과 Java 사이의 속도 차이)의 속도 차이를 나타내는 것이 아닙니다. 이것은 언어 자체가 아닌 다른 언어로 컴파일 된이 두 가지 구현을 비교하는 대표적인 사례입니다.

즉, 이와 같이 두 응용 프로그램을 비교해도 동일한 경우에도 언어 (또는 컴파일러)를 속도와 비교하지는 않습니다. 동일한 알고리즘의 매우 다른 버전을 실행하는 두 개의 서로 다른 프로그램을 단순히 비교합니다. 특별한 경우입니다.

C 코드가 컴파일됩니다. 그것은 서로 다른 하드웨어 (예를 들어 두 개의 프로세서 대 네 개의 프로세서)에서 서로 다른 성능 특성을 갖습니다.

Java 코드가 바이트 컴파일됩니다. 코드가 실행될 플랫폼과 가장 잘 일치하도록 대부분의 Java VM에서 실행하기 전에 최적화됩니다.

결국 Java보다 C 또는 C++에서 더 적극적으로 최적화 할 수 있습니다. Java에서 일치 할 수없는 C 또는 C++ 코드를 얻을 수있는 것보다 성능이 중요한 코드를 작성해야하는 경우, 이는 Java VM 자체를 실행하는 데 필요한 속도 임계 값보다 빠르기 때문입니다.

이러한 최적화는 특별한 경우 프로파일 링 및 최적화에 많은 시간과 노력이 소요되지만 대부분의 응용 프로그램 도메인에서는 필요하지 않습니다. 당신이 그 수준의 수행 능력을 필요로하는지 모른다면 아마 그렇지 않을 것입니다.

4

C 버전에는 불필요한 많은 메모리가 할당되어있어 비교적 비쌉니다. 당신이 사용하는 경우 인플레 이스 (in-place) 추가 및 증가 기능 (아래 참조)이 크게 성능 향상시킬 수 있습니다 : 인 장소 추가는 = 7.6 초를 사용

  • 원래 C 코드 = 200 초
  • = 6.5 초를 제자리 추가 및 단위 사용
속도 차가의 수를 감소 전적으로 제공

원래 코드의 2 억 5 천만 (!)에서 수정 된 코드의 30 번째까지입니다. 따라서 원래의 코드는 실제로는 실제 bignum 곱셈 알고리즘이 아닌 각 언어로 메모리 관리자의 효율성을 측정하는 것입니다.

Java 코드에서 이와 동일한 최적화를 사용하면 비슷한 속도 향상을 얻을 수 있습니다. 벤치마킹/최적화 게임을 너무 많이하는 것을 조심하십시오. 특정 버전에 대한 충분한 작업을 통해 다른 버전보다 더 빨리 작업을 수행 할 수 있습니다. 일반적으로 언어 결정은 에 "X가 1 % 더 빠름" 일뿐입니다.

은 단순히 당신의 곱셈 루프 변경 새로운에서 적절한 기능을 사용하려면 사용

for (; BUI_cmp(i, a) < 0; BUI_inc(i)) 
    { 
     BUI_addinplace(c, b); 
    } 

인플레 이스 (in-place) 추가/증분 기능을 다음과 같습니다 :로 쉽게 언급

void BUI_addinplace(BUI a, const BUI b) //a += b 
{ 
    int maxSize = MAX(a->len, b->len) + 1; 

    if (a->num.len < maxSize) 
    { 
     Array tmp; 
     new_Array(&tmp, maxSize); 

     memset(tmp.data, 0, tmp.len); 
     memcpy(tmp.data, a->num.data, a->len); 

     del_Array(&a->num); 
     a->num = tmp; 
    } 

    for (int i = 0; i < b->len; ++i) 
    { 
     a->num.data[i] += b->num.data[i]; 
    } 

    int maxLen = a->len; 

    for (int i = 0; i < a->len; ++i) 
    { 
     if (a->num.data[i] >= 10) 
     { 
      a->num.data[i + 1] += a->num.data[i]/10; 
      a->num.data[i] %= 10; 
      maxLen = i + 2; 
     } 
    } 

    if (maxLen > a->len) a->len = maxLen; 
} 


void BUI_inc(BUI a) //a += 1 
{ 
    int maxSize = a->len + 1; 

    if (a->num.len < maxSize) 
    { 
     ++numAllocations; 
     Array tmp; 
     new_Array(&tmp, maxSize); 

     memset(tmp.data, 0, tmp.len); 
     memcpy(tmp.data, a->num.data, a->len); 

     del_Array(&a->num); 
     a->num = tmp; 
    } 

    ++a->num.data[0]; 
    if (a->num.data[0] < 10) return; 

    int maxLen = a->len; 

    for (int i = 0; i < a->len; ++i) 
    { 
     if (a->num.data[i] >= 10) 
     { 
      a->num.data[i + 1] += a->num.data[i]/10; 
      a->num.data[i] %= 10; 
      maxLen = i + 2; 
     } 
     else { 
      break; 
     } 
    } 

    if (maxLen > a->len) a->len = maxLen; 
} 
+0

수정 해 주셔서 감사합니다. 그리고 맞습니다. 각 언어의 메모리 할당자를 테스트하고있었습니다. 나는 기가 바이트의 메모리를 소비하는 거대한 나무를 생성하는 프로그램을 작성할 계획이므로, 불필요하게 많은 양의 메모리를 할당하기위한 테스트 코드를 작성하는 것이 목적이다. 'malloc()'함수를 너무 많이 믿는 것은 내 잘못이었다. 나는 C 버전을위한 커스텀 할당자를 가지고 메모리 풀을 만들고 그것을 어떻게 보는지 다시 테스트 할 것이다. – xiver77

+0

예,'malloc()'은 비교적 벙어리이며 작은 할당/할당 해제의 "많은"작업에는 적합하지 않습니다. 일반적으로 응용 프로그램에 맞게 조정 된 고유 한 메모리 할당자를 만들어 대용량 성능을 향상시킬 수 있습니다. – uesp

3

댓글 및 기타 답변에서 병목 현상은 malloc에 ​​대한 2 억 5 천만 건의 호출입니다.

나는 보통 C를 쓰지 않기 때문에 비 관용적 인 코드를 사용하지 않겠다.하지만 매우 원시적 인 할당 자다. (많은 제한이 있으며 여전히 버그와 쉬운 기회가있을 수 있으며 몇 가지 주장을 사용할 수있다.) 이 경우 malloc보다 훨씬 뛰어난 성능을 보입니다. 나는이 함께 얻을

#define BUFFER_SIZE 1048576 //reserve 1MB, although we only use 417bytes 

typedef struct mem_block_hdr 
{ 
    unsigned short size; //size of the memory block, excluding header 
    char free;   //is this block free 
    char cont;   //not used 
}mem_block_hdr_t; 

struct _myallocdata 
{ 
    char mem[BUFFER_SIZE]; 
    unsigned int highest_header_pos; 
} my_alloc_data; 

void init_block_hdr(void *mem) 
{ 
    mem_block_hdr_t *head = (mem_block_hdr_t*) mem; 
    head->size = USHRT_MAX; 
    head->free = 1; 
    head->cont = 0; 
} 
void init_my_alloc() 
{ 
    init_block_hdr(my_alloc_data.mem); 
    my_alloc_data.highest_header_pos = 0; 
} 

mem_block_hdr_t *next_header(mem_block_hdr_t *curr) 
{ 
    return (mem_block_hdr_t*) ((char*) curr + sizeof(mem_block_hdr_t) + curr->size); 
} 

mem_block_hdr_t *find_next_free(unsigned int size) 
{ 
    void * ret; 
    char end_reached = 0; 
    mem_block_hdr_t *head = (mem_block_hdr_t*) my_alloc_data.mem; 

    while(
     (!head->free || head->size < size) && 
     ((char*) head - my_alloc_data.mem) < my_alloc_data.highest_header_pos 
     ) 
    { 
     head = next_header(head); 
    } 
    return head; 
} 

void *my_alloc(unsigned int size) 
{ 
    mem_block_hdr_t *header = find_next_free(size); 
    unsigned int diff = (char*) header - my_alloc_data.mem ; 

    if (header->size == USHRT_MAX) 
    { 
     header->size = size; 
    } 
    header->free = 0; 

    if (diff >= my_alloc_data.highest_header_pos) 
    { 
     mem_block_hdr_t *new_high = next_header(header); 
     init_block_hdr(new_high); 
     my_alloc_data.highest_header_pos = ((char*) new_high) - my_alloc_data.mem; 
    } 
    return (void *)++header; 
} 

void my_free(void *mem) 
{ 
    mem_block_hdr_t *hdr =(mem_block_hdr_t *) ((char *)mem - sizeof(mem_block_hdr_t)); 
    hdr->free = 1; 
} 

void new_Array(Array *a, int len) 
{ 
    //a->data = (byte *) malloc(len * sizeof(byte)); 
    a->data = (byte *) my_alloc(len * sizeof(byte)); 
    a->len = len; 
} 

void del_Array(Array *a) 
{ 
    //free(a->data); 
    my_free(a->data); 
} 

//calling init_my_alloc() in main before using it 

번호는 다음과 같습니다

123456789 x 987654321 = 121932631112635269 
elapsed time (custom alloc): 25.546 
19 18 
123456789 x 987654321 = 121932631112635269 
elapsed time (malloc): 290.118 
19 18 

편집 : 나는 GCC를 사용하여, malloc을 호출에 매우 친절하지 MSVC 플래그를 고른 것 같다 나는이 얻을 :

123456789 x 987654321 = 121932631112635269 
elapsed time (custom alloc): 30.703 
19 18 
123456789 x 987654321 = 121932631112635269 
elapsed time (malloc): 46.406 
19 18 

할당 자 구현이 많습니다. (때로는 모든 큰 C 프로젝트가 자체적으로 가지고있는 것처럼 보이기 때문에) 기꺼이 할 수 있습니다.

어쨌든 Java를 작성하는 것이 더 편하다고 생각하고 용도에 충분하다고 생각하면 사용하지 않아도됩니다. 프로그래밍 언어는 결국 도구 일뿐입니다.