2015-02-02 15 views
10

Tôi đang làm một thử nghiệm: so sánh thời gian truy tìm của cgo và các hàm Go thuần túy chạy 100 triệu lần mỗi lần. Hàm cgo mất nhiều thời gian hơn so với hàm Golang, và tôi bị nhầm lẫn với kết quả này. thử nghiệm mã của tôi là:Tại sao hiệu suất của cgo quá chậm? có điều gì sai với mã thử nghiệm của tôi không?

package main 

import (
    "fmt" 
    "time" 
) 

/* 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

void show() { 

} 

*/ 
// #cgo LDFLAGS: -lstdc++ 
import "C" 

//import "fmt" 

func show() { 

} 

func main() { 
    now := time.Now() 
    for i := 0; i < 100000000; i = i + 1 { 
     C.show() 
    } 
    end_time := time.Now() 

    var dur_time time.Duration = end_time.Sub(now) 
    var elapsed_min float64 = dur_time.Minutes() 
    var elapsed_sec float64 = dur_time.Seconds() 
    var elapsed_nano int64 = dur_time.Nanoseconds() 
    fmt.Printf("cgo show function elasped %f minutes or \nelapsed %f seconds or \nelapsed %d nanoseconds\n", 
     elapsed_min, elapsed_sec, elapsed_nano) 

    now = time.Now() 
    for i := 0; i < 100000000; i = i + 1 { 
     show() 
    } 
    end_time = time.Now() 

    dur_time = end_time.Sub(now) 
    elapsed_min = dur_time.Minutes() 
    elapsed_sec = dur_time.Seconds() 
    elapsed_nano = dur_time.Nanoseconds() 
    fmt.Printf("go show function elasped %f minutes or \nelapsed %f seconds or \nelapsed %d nanoseconds\n", 
     elapsed_min, elapsed_sec, elapsed_nano) 

    var input string 
    fmt.Scanln(&input) 
} 

và kết quả là:

cgo show function elasped 0.368096 minutes or 
elapsed 22.085756 seconds or 
elapsed 22085755775 nanoseconds 

go show function elasped 0.000654 minutes or 
elapsed 0.039257 seconds or 
elapsed 39257120 nanoseconds 

Kết quả cho thấy cách gọi chức năng C là chậm hơn so với chức năng Go. Có điều gì sai với mã thử nghiệm của tôi không?

hệ thống của tôi là: mac OS X 10.9.4 (13E28)

+0

Tại sao bạn cho rằng gọi một hàm C từ Go nên nhanh hơn gọi hàm Go từ Go? – Volker

+0

Tôi mong đợi mã đi vào nội tuyến phiên bản go của 'show()' là một lợi thế hơn nữa cho mã đi trên 'C.show()'. –

Trả lời

25

Như bạn đã khám phá, có phí khá cao khi gọi mã C/C++ qua CGo. Vì vậy, nói chung, bạn nên cố gắng giảm thiểu số lượng cuộc gọi CGo mà bạn thực hiện. Đối với ví dụ trên, thay vì gọi một hàm CGo liên tục trong một vòng lặp, điều đó có thể có ý nghĩa khi di chuyển vòng xuống tới C.

Có một số khía cạnh về cách thời gian chạy thiết lập chủ đề có thể phá vỡ kỳ vọng của nhiều phần mã C:

  1. Goroutines chạy trên một chồng tương đối nhỏ, xử lý tăng trưởng ngăn xếp thông qua ngăn xếp phân đoạn (phiên bản cũ) hoặc bằng cách sao chép (phiên bản mới).
  2. Chủ đề được tạo bởi thời gian chạy Go có thể không tương tác đúng với việc triển khai bộ nhớ cục bộ của libpthread.
  3. Trình xử lý tín hiệu UNIX của trình chạy đi có thể ảnh hưởng đến mã C hoặc C++ truyền thống.
  4. Tiếp tục sử dụng lại các chuỗi hệ điều hành để chạy nhiều Goroutines. Nếu mã C được gọi là một cuộc gọi hệ thống chặn hoặc cách khác độc quyền các chủ đề, nó có thể gây bất lợi cho các goroutine khác.

Vì những lý do này, CGo chọn cách tiếp cận an toàn để chạy mã C trong một chuỗi riêng biệt được thiết lập với ngăn xếp truyền thống.

Nếu bạn đến từ các ngôn ngữ như Python, thì việc viết lại các điểm nóng mã trong C là không phổ biến như một cách để tăng tốc một chương trình bạn sẽ thất vọng. Nhưng đồng thời, có một khoảng cách nhỏ hơn nhiều về hiệu suất giữa mã C và Go tương đương.

Nói chung tôi dự trữ CGo để giao tiếp với các thư viện hiện có, có thể với các hàm bao bọc C nhỏ có thể giảm số lượng cuộc gọi tôi cần thực hiện từ Go.

+0

Cảm ơn, nó giúp tôi rất nhiều! –

+0

Điều này có thể đã lỗi thời: https://groups.google.com/forum/#!topic/golang-nuts/RTtMsgZi88Q – gavv

-1

Có một ít chi phí trong việc kêu gọi các chức năng C từ Go. Điều này không thể thay đổi.

8

Cập nhật cho James's answer: có vẻ như không có chuyển đổi luồng trong thực hiện hiện tại.

Xem this thread trên golang-hạt:

Có luôn sẽ có một số chi phí. Nó đắt hơn một cuộc gọi hàm đơn giản nhưng ít tốn kém hơn nhiều so với công tắc ngữ cảnh (agl đang ghi nhớ thực hiện trước đó; chúng tôi cắt công tắc chỉ trước khi phát hành công khai). Ngay bây giờ chi phí về cơ bản chỉ cần thực hiện chuyển đổi bộ đăng ký đầy đủ (không có sự tham gia của nhân). Tôi đoán nó có thể so sánh với mười cuộc gọi chức năng.

Xem thêm this answer liên kết bài đăng blog "cgo is not Go".

C không biết gì về quy ước gọi của Go hoặc ngăn xếp có thể phát triển, do đó cuộc gọi đến mã C phải ghi lại tất cả chi tiết của goroutine stack, chuyển sang ngăn xếp C và chạy mã C không có kiến thức về cách nó được gọi, hoặc thời gian chạy Go lớn hơn phụ trách chương trình.

Vì vậy, bản ngã có chi phí cao vì nó thực hiện chuyển đổi ngăn xếp ngăn xếp, chứ không phải công tắc chủ đề.

Nó lưu và khôi phục tất cả đăng ký khi hàm C được gọi, trong khi không bắt buộc khi chức năng Go hoặc chức năng lắp ráp được gọi.


Bên cạnh đó, công ước gọi CGO cấm đi Go con trỏ trực tiếp vào mã C, và phương pháp phổ biến là sử dụng C.malloc, và do đó giới thiệu bổ sung cân đối. Xem this question để biết chi tiết.

Các vấn đề liên quan