2012-02-29 26 views
7

Vì vậy, tôi đã chơi xung quanh với một số mã và muốn xem phương pháp chuyển đổi một std :: string to upper case là hiệu quả nhất. Tôi nghĩ rằng hai người sẽ có phần tương tự như hiệu suất-khôn ngoan, nhưng tôi đã sai lầm khủng khiếp. Bây giờ tôi muốn tìm hiểu lý do tại sao. Phương pháp đầu tiên để chuyển đổi chuỗi hoạt động như sau: cho mỗi ký tự trong chuỗi (lưu độ dài, lặp lại từ 0 đến chiều dài), nếu nó nằm giữa 'a' và 'z', sau đó dịch chuyển nó sao cho nó giữa 'A' và 'Z' thay thế. Phương thức thứ hai hoạt động như sau: đối với mỗi ký tự trong chuỗi (bắt đầu từ 0, tiếp tục cho đến khi chúng ta nhấn một terminator null), áp dụng hàm xây dựng trong hàm toupper().Chuyển đổi std :: chuỗi thành chữ hoa: sự khác biệt hiệu suất chính?

Dưới đây là các mã:

#include <iostream> 
#include <string> 

inline std::string ToUpper_Reg(std::string str) 
{ 
    for (int pos = 0, sz = str.length(); pos < sz; ++pos) 
    { 
     if (str[pos] >= 'a' && str[pos] <= 'z') { str[pos] += ('A' - 'a'); } 
    } 

    return str; 
} 

inline std::string ToUpper_Alt(std::string str) 
{ 
    for (int pos = 0; str[pos] != '\0'; ++pos) { str[pos] = toupper(str[pos]); } 

    return str; 
} 


int main() 
{ 
    std::string test = " [email protected]#$%^&*()_+=-`'{}[]\\|\";:<>,./?"; 

    for (size_t i = 0; i < 100000000; ++i) { ToUpper_Reg(test); /* ToUpper_Alt(test); */ } 

    return 0; 
} 

Phương pháp đầu tiên ToUpper_Reg mất khoảng 169 giây mỗi 100 triệu lần lặp lại.
Phương pháp thứ hai Toupper_Alt mất khoảng 379 giây cho mỗi 100 triệu lần lặp lại.

Điều gì mang lại?


Edit: Tôi đã thay đổi phương pháp thứ hai để nó lặp chuỗi như thế nào là người đầu tiên không (thiết lập độ dài sang một bên, vòng lặp while ít hơn chiều dài) và đó là một chút nhanh hơn, nhưng vẫn còn khoảng gấp đôi chậm.


Sửa 2: Cảm ơn tất cả mọi người nhận hồ sơ của bạn! Các dữ liệu tôi sẽ sử dụng nó trên được đảm bảo là ascii, vì vậy tôi nghĩ rằng tôi sẽ gắn bó với phương pháp đầu tiên trong thời gian này. Tôi sẽ ghi nhớ rằng toupper là ngôn ngữ cụ thể khi nào/nếu tôi cần.

+7

toupper là chậm hơn so với những gì bạn làm trong _Reg bởi vì nó không nhiều hơn bạn làm trong Reg? – Almo

+4

Tại sao bạn cũng không thêm phép chuyển đổi chuẩn C++ tại chỗ, 'std :: transform (s.begin(), s.end(), s.begin(), (int (*) (int)) std :: toupper); '? (Bạn cần phải "#include ', '' và ''.) –

+0

Wow, đó là một ngụm. Ngoài sự tò mò, phần '(int (*) (int))' là gì? –

Trả lời

13

std::toupper sử dụng ngôn ngữ hiện tại để thực hiện chuyển đổi trường hợp, bao gồm gọi hàm và các phép trừu tượng khác. Vì vậy, tự nhiên, nó sẽ chậm hơn. Nhưng nó cũng sẽ hoạt động trên văn bản không phải ASCII.

3

toupper() tính đến ngôn ngữ để nó có thể xử lý (một số) ký tự quốc tế và phức tạp hơn nhiều so với chỉ xử lý phạm vi ký tự 'a' - 'z'.

5

toupper() không chỉ thay đổi các ký tự trong phạm vi [a-z]. Đối với một điều nó phụ thuộc vào miền địa phương và có thể xử lý nhiều hơn chỉ ASCII.

0

Thứ hai liên quan đến cuộc gọi hàm. một cuộc gọi hàm là một hoạt động tốn kém trong một vòng lặp bên trong. toupper cũng sử dụng ngôn ngữ để xác định cách thay đổi ký tự.

Các tiến bộ của cuộc gọi là nó là tiêu chuẩn và sẽ làm việc bất kể mã hóa ký tự trên máy chủ

Điều đó nói rằng, tôi rất muốn giới thiệu sử dụng chức năng tăng:

boost::algorithm::to_upper 

Đó là một mẫu có nhiều khả năng được gạch chân hơn, tuy nhiên nó có liên quan đến ngôn ngữ. Tôi vẫn sẽ sử dụng nó.

http://www.boost.org/doc/libs/1_40_0/doc/html/boost/algorithm/to_upper.html

0

Tôi đoán đó là vì một giây gọi một hàm thư viện chuẩn C, mà trên một mặt không được sắp xếp theo hàng, do đó bạn có những phí của một cuộc gọi chức năng. Nhưng quan trọng hơn, chức năng này có thể làm nhiều hơn hai so sánh, hai bước nhảy và hai bổ sung nguyên. Nó thực hiện kiểm tra bổ sung về nhân vật và đưa miền địa phương hiện tại vào tài khoản và tất cả những thứ đó.

3

Vâng, ToUpper_Reg() không hoạt động. Ví dụ, nó không biến tên của tôi thành tất cả các ký tự viết hoa. Điều đó nói rằng, ToUpper_Alt() cũng không hoạt động vì nó toupper() được chuyển một giá trị âm trên một số nền tảng, tức là nó tạo ra hành vi không xác định (thường là sự cố) khi sử dụng nó với tên của tôi. Đây là một cách dễ dàng cố định, tuy nhiên, bằng cách gọi một cách chính xác nó một cái gì đó như thế này:

toupper(static_cast<unsigned char>(str[pos])) 

Điều đó nói rằng, hai phiên bản của mã không phải là tương đương: phiên bản onot sử dụng toupper() không bằng văn bản cho nhân vật tất cả các thời gian trong khi phiên bản thứ hai là: một khi mọi thứ được chuyển thành chữ hoa, nó luôn luôn có cùng nhánh sau khi thử nghiệm và sau đó không làm gì cả. Bạn có thể muốn thay đổi ToUpper_Alt() trông như thế này và kiểm tra lại:

inline std::string ToUpper_Alt(std::string str) 
{ 
    for (int pos = 0; str[pos] != '\0'; ++pos) { 
     if (islower(static_cast<unsigned char>(str[pos])) { 
      str[pos] = toupper(static_cast<unsigned char>(str[pos])); 
     } 
    } 

    return str; 
} 

tôi sẽ đoán sự khác biệt là chữ viết: toupper() nghề so sánh cho một mảng nhìn lên. Miền địa phương được truy cập nhanh chóng và tất cả toupper() là lấy con trỏ hiện tại và truy cập vị trí tại một giá trị đã cho. Với dữ liệu trong bộ nhớ cache, điều này có thể nhanh như nhánh.

+0

Đẹp bắt sự khác biệt về số lượng viết. –

+0

Có tốn kém khi xây dựng một miền địa phương mới cho mọi cuộc gọi đến 'std :: toupper'? Nếu người dùng thường lưu vào bộ nhớ cache một đối tượng địa phương để chuyển vào thay thế? – caps

+0

@caps: khá tốn kém để tạo đối tượng 'std :: locale' mới. Bản sao yêu cầu truy cập số gia tăng tham chiếu được đồng bộ hóa.Việc tạo một 'std :: locale' mới và thay đổi một khía cạnh đòi hỏi một truy cập tính toán tham chiếu đồng bộ bổ sung trên mỗi khía cạnh. Mặc định việc xây dựng một 'std :: locale' cần truy cập đồng bộ vào toàn cục' std :: locale' cộng với chi phí của một bản sao. Vì vậy, có, bạn nên giữ các đối tượng 'std :: locale' xung quanh. Lưu ý rằng phiên bản 'std :: toupper()' không sử dụng tham số 'std :: locale' không tạo một tham số: nó truy cập vào thực thể miền địa phương của C thay vào đó! –

0

std :: toupper sử dụng ngôn ngữ hiện tại và lý do tại sao điều này chậm hơn hàm C là ngôn ngữ hiện tại được chia sẻ và có thể thay đổi từ các chủ đề khác nhau, vì vậy cần khóa đối tượng địa phương khi được truy cập đảm bảo nó không được chuyển trong khi gọi. Điều này xảy ra một lần cho mỗi cuộc gọi đến toupper và giới thiệu khá lớn trên không (có được khóa có thể yêu cầu một syscall tùy thuộc vào thực hiện). Một cách giải quyết nếu bạn muốn có được hiệu suất và tôn trọng miền địa phương là để có được đối tượng locale đầu tiên (tạo một bản sao cục bộ) và sau đó gọi khía cạnh toupper trên bản sao của bạn, do đó tránh sự cần thiết phải khóa cho mỗi cuộc gọi toupper. Xem liên kết bên dưới để biết ví dụ.

http://www.cplusplus.com/reference/std/locale/ctype/toupper/

0

Câu hỏi đặt ra đã được trả lời, nhưng như một sang một bên, thay thế ruột của vòng lặp của bạn trong phương pháp đầu tiên với:

std::string::value_type &c = str[pos]; 
if ('a' <= c && c <= 'z') { c += ('A' - 'a'); } 

làm cho nó thậm chí còn nhanh hơn. Có lẽ trình biên dịch của tôi chỉ hút.

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