2011-07-03 33 views
5

Giả sử rằng chức năngChức năng cần mảng riêng cho không gian làm việc - thực hành tốt nhất?

void foo(int n, double x[]) 

loại n-vector x, hiện một số hoạt động trên x, và sau đó khôi phục lại trật tự ban đầu để x trước khi trở về. Vì vậy, nội bộ, foo cần một số bộ nhớ tạm thời, ví dụ: ít nhất một vectơ của số nguyên sao cho nó lưu trữ thứ tự ban đầu.

Cách tốt nhất để xử lý bộ nhớ tạm thời này là gì? Tôi có thể nghĩ đến hai cách tiếp cận rõ ràng:

  1. foo tuyên bố không gian làm việc riêng của mình bằng cách tuyên bố một mảng nội bộ, tức là ở phía trên cùng của foo chúng tôi có

    int temp[n]; 
    
  2. trong các thói quen gọi điện thoại chính, tự động phân bổ n-vectơ của ints một lần và chuyển vào bộ nhớ trong mỗi cuộc gọi đến phiên bản foo chấp nhận bộ nhớ tạm thời dưới dạng số 3 arg, tức là,

    double *temp = malloc(n*sizeof(double)); 
    foo(n, x, temp); 
    

Tôi lo lắng rằng phương án 1 là không hiệu quả (chức năng foo sẽ được gọi nhiều lần với cùng n), và phương án 2 là chỉ đơn giản xấu xí, vì tôi phải mang theo lưu trữ tạm thời này để nó luôn có sẵn ở bất cứ nơi nào tôi cần để gọi đến foo(n,x).

Có các tùy chọn thanh lịch nào khác không?

Trả lời

5

Nếu bạn kết thúc bằng tùy chọn 2 - tức là, hàm sử dụng bộ nhớ được cấp phát ở nơi khác - sử dụng đóng gói thích hợp.

Tóm lại, không chuyển vào mảng thô, chuyển vào đối tượng context có các hàm initrelease.

Sau đó, người dùng vẫn phải vượt qua trong ngữ cảnh và thiết lập đúng và xé nó ra nhưng chi tiết bị ẩn khỏi cô ấy và cô ấy không quan tâm đến chi tiết phân bổ. Đây là một mẫu phổ biến trong C.

typedef struct { 
    double* storage; 
} foo_context; 

void foo_context_init(foo_context*, int n); 

void foo_context_free(foo_context*); 

void foo(foo_context* context, int n, double x[]); 

Bây giờ, trong trường hợp rất đơn giản, đây rõ ràng là một khoản phí khổng lồ và tôi đồng ý với Oli rằng tùy chọn 1 được yêu cầu.

+0

+1. BTW, nếu lưu trữ là 'n' cụ thể,' int n' có thể được cuộn vào ngữ cảnh, để rút ngắn danh sách đối số thành 'foo()'. –

5

Tùy chọn 1 rõ ràng là sạch nhất (vì nó được đóng gói hoàn toàn). Vì vậy, đi với Option 1 cho đến khi profiling đã xác định rằng đây là một nút cổ chai.

Cập nhật

@ comment R của dưới đây là chính xác; điều này có thể thổi xếp chồng của bạn nếu n lớn. Phương thức "đóng gói" trước C99 sẽ là để tạo thành mảng nội bộ, thay vì đặt nó trên ngăn xếp.

+1

Tùy chọn 1 cực kỳ nguy hiểm trừ khi bạn biết 'n' luôn là" nhỏ ". –

+0

Trong ứng dụng tôi đang xem xét, 'n' có khả năng là 1e6 hoặc 1e7, và' foo' có thể được gọi là hàng trăm lần. Tôi cho rằng điều này là quá đắt. – Michael

4

Trên hầu hết các tùy chọn kiến ​​trúc 1 là rất hiệu quả vì nó phân bổ bộ nhớ trên ngăn xếp và thường là một bổ sung vào ngăn xếp và/hoặc con trỏ khung. Chỉ cần cẩn thận không làm n quá lớn.

+0

Cũng lưu ý rằng đây là C99 cụ thể. Trước C99, điều này sẽ cần phải được thực hiện với 'malloc'. –

+0

@Oli Charlesworth: Tốt. –

+0

Pre-C99 nó sẽ được thực hiện với 'int temp [MAX_N];' trong đó 'MAX_N' là một giới hạn trên' n'. Nếu 'n' không được gắn kết, VLA không an toàn và bạn vẫn phải dùng' malloc'. Vì vậy, C99 không thay đổi gì về vấn đề này. –

3

Như Oli đã nói trong câu trả lời của mình, tốt nhất là có chức năng tự chủ về mảng tạm thời này. Một phân bổ đơn lẻ sẽ không tốn nhiều chi phí, trừ khi hàm đó được gọi trong một vòng lặp rất nhanh ... vì vậy hãy lấy nó ngay trước, sau đó lược tả và sau đó quyết định xem nó có đáng làm tối ưu hay không.

Điều đó nói rằng trong một vài trường hợp sau khi hồ sơ và khi cấu trúc dữ liệu tạm thời cần là một chút phức tạp hơn mà một mảng int duy nhất tôi đã thông qua phương pháp sau đây:

void foo(int n, ... other parameters ...) 
{ 
    static int *temp_array, temp_array_size; 
    if (n > temp_array_size) 
    { 
     /* The temp array we have is not big enough, increase it */ 
     temp_array = realloc(temp_array, n*sizeof(int)); 
     if (!temp_array) abort("Out of memory"); 
     temp_array_size = n; 
    } 
    ... use temp_array ... 
} 

lưu ý rằng việc sử dụng một quy tắc mảng tĩnh ra ví dụ như đa luồng hoặc đệ quy và điều này cần được ghi rõ trong tài liệu.

+0

Ngoài các vấn đề điển hình với các biến 'tĩnh', một điều khác cần lưu ý là kích thước của bộ đệm tạm thời là không bị chặn và không bao giờ giảm. http://blogs.msdn.com/b/oldnewthing/archive/2006/05/02/588350.aspx – jamesdlin

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