Là tiêu chuẩn C ++ bắt buộc hiệu suất kém cho iostreams, hay tôi chỉ đang xử lý một triển khai kém?


197

Mỗi lần tôi đề cập đến hiệu năng chậm của iostreams thư viện chuẩn C ++, tôi lại gặp một làn sóng hoài nghi. Tuy nhiên, tôi có kết quả trình hồ sơ cho thấy số lượng lớn thời gian dành cho mã thư viện iostream (tối ưu hóa trình biên dịch đầy đủ) và chuyển từ iostream sang API I / O dành riêng cho hệ điều hành và quản lý bộ đệm tùy chỉnh giúp cải thiện độ lớn.

Thư viện chuẩn C ++ đang làm thêm công việc gì, có phải là yêu cầu của tiêu chuẩn và nó có hữu ích trong thực tế không? Hoặc một số trình biên dịch cung cấp triển khai các iostream có khả năng cạnh tranh với quản lý bộ đệm thủ công?

Điểm chuẩn

Để giải quyết vấn đề, tôi đã viết một vài chương trình ngắn để thực hiện bộ đệm nội bộ của iostreams:

Lưu ý rằng ostringstreamstringbufcác phiên bản chạy ít lần lặp hơn vì chúng chậm hơn rất nhiều.

Trên ideone, ostringstreamtốc độ chậm hơn khoảng 3 lần so với std:copy+ back_inserter+ std::vectorvà chậm hơn khoảng 15 lần so với memcpyvào bộ đệm thô. Điều này cảm thấy phù hợp với hồ sơ trước và sau khi tôi chuyển ứng dụng thực sự của mình sang bộ đệm tùy chỉnh.

Đây đều là những bộ đệm trong bộ nhớ, do đó, sự chậm chạp của iostreams không thể đổ lỗi cho I / O đĩa chậm, quá nhiều lỗi, đồng bộ hóa với stdio hoặc bất kỳ điều gì khác mà mọi người sử dụng để bào chữa cho sự chậm chạp của thư viện chuẩn C ++ iostream.

Sẽ thật tuyệt khi thấy điểm chuẩn trên các hệ thống khác và bình luận về những việc triển khai phổ biến (như libc ++ của gcc, Visual C ++, Intel C ++) và bao nhiêu chi phí bắt buộc theo tiêu chuẩn.

Lý do cho bài kiểm tra này

Một số người đã chỉ ra một cách chính xác rằng iostreams thường được sử dụng cho đầu ra được định dạng. Tuy nhiên, chúng cũng là API hiện đại duy nhất được cung cấp theo tiêu chuẩn C ++ để truy cập tệp nhị phân. Nhưng lý do thực sự để thực hiện các bài kiểm tra hiệu năng trên bộ đệm nội bộ áp dụng cho I / O được định dạng điển hình: nếu iostreams không thể giữ bộ điều khiển đĩa được cung cấp dữ liệu thô, làm sao chúng có thể theo kịp khi chúng cũng chịu trách nhiệm định dạng?

Thời gian chuẩn

Tất cả những điều này là mỗi lần lặp của kvòng lặp ngoài ( ).

Trên ideone (gcc-4.3.4, hệ điều hành và phần cứng không xác định):

  • ostringstream: 53 mili giây
  • stringbuf: 27 ms
  • vector<char>back_inserter: 17,6 ms
  • vector<char> với trình vòng lặp thông thường: 10,6 ms
  • vector<char> kiểm tra vòng lặp và giới hạn: 11,4 ms
  • char[]: 3,7 ms

Trên máy tính xách tay của tôi (Visual C ++ 2010 x86, cl /Ox /EHscWindows 7 Ultimate 64-bit, Intel Core i7, RAM 8 GB):

  • ostringstream: 73,4 mili giây, 71,6 ms
  • stringbuf: 21,7 ms, 21,3 ms
  • vector<char>back_inserter: 34,6 ms, 34,4 ms
  • vector<char> với trình vòng lặp thông thường: 1,10 ms, 1,04 ms
  • vector<char> Kiểm tra vòng lặp và giới hạn: 1,11 ms, 0,87 ms, 1,12 ms, 0,89 ms, 1,02 ms, 1,14 ms
  • char[]: 1,48 ms, 1,57 ms

Visual C ++ 2010 x86, với sơ-Guided Tối ưu hóa cl /Ox /EHsc /GL /c, link /ltcg:pgi, chạy, link /ltcg:pgo, biện pháp:

  • ostringstream: 61,2 ms, 60,5 ms
  • vector<char> với trình vòng lặp thông thường: 1,04 ms, 1,03 ms

Cùng một máy tính xách tay, cùng một hệ điều hành, sử dụng cygwin gcc 4.3.4 g++ -O3:

  • ostringstream: 62,7 ms, 60,5 ms
  • stringbuf: 44,4 ms, 44,5 ms
  • vector<char>back_inserter: 13,5 ms, 13,6 ms
  • vector<char> với trình vòng lặp thông thường: 4,1 ms, 3,9 ms
  • vector<char> Kiểm tra vòng lặp và giới hạn: 4.0 ms, 4.0 ms
  • char[]: 3,57 ms, 3,75 ms

Cùng một máy tính xách tay, Visual C ++ 2008 SP1 , cl /Ox /EHsc:

  • ostringstream: 88,7 ms, 87,6 ms
  • stringbuf: 23,3 ms, 23,4 ms
  • vector<char>back_inserter: 26,1 ms, 24,5 ms
  • vector<char> với trình vòng lặp thông thường: 3,13 ms, 2,48 ms
  • vector<char> Kiểm tra vòng lặp và giới hạn: 2,97 ms, 2,53 ms
  • char[]: 1,52 ms, 1,25 ms

Cùng một máy tính xách tay, trình biên dịch 64 bit Visual C ++ 2010:

  • ostringstream: 48,6 ms, 45,0 ms
  • stringbuf: 16,2 ms, 16,0 ms
  • vector<char>back_inserter: 26,3 ms, 26,5 ms
  • vector<char> với trình vòng lặp thông thường: 0,87 ms, 0,89 ms
  • vector<char> Kiểm tra vòng lặp và giới hạn: 0,99 ms, 0,99 ms
  • char[]: 1,25 ms, 1,24 ms

EDIT: Ran tất cả hai lần để xem kết quả phù hợp như thế nào. IMO khá nhất quán.

LƯU Ý: Trên máy tính xách tay của tôi, vì tôi có thể dành nhiều thời gian CPU hơn ideone cho phép, tôi đặt số lần lặp là 1000 cho tất cả các phương thức. Điều này có nghĩa là ostringstreamvectorviệc tái phân bổ, chỉ diễn ra ở lần đầu tiên, sẽ ít ảnh hưởng đến kết quả cuối cùng.

EDIT: Rất tiếc, đã tìm thấy một lỗi trong vector-with-normal-iterator, iterator không được nâng cao và do đó có quá nhiều lần truy cập bộ đệm. Tôi đã tự hỏi làm thế nào vector<char>là tốt hơn char[]. Mặc dù vậy, nó không tạo ra nhiều khác biệt, vector<char>vẫn nhanh hơn so với char[]VC ++ 2010.

Kết luận

Việc đệm các luồng đầu ra đòi hỏi ba bước mỗi lần dữ liệu được thêm vào:

  • Kiểm tra xem khối đến phù hợp với không gian bộ đệm có sẵn.
  • Sao chép khối đến.
  • Cập nhật con trỏ cuối dữ liệu.

Đoạn mã mới nhất tôi đã đăng, " vector<char>kiểm tra đơn giản cộng với kiểm tra giới hạn" không chỉ thực hiện điều này, nó còn phân bổ không gian bổ sung và di chuyển dữ liệu hiện có khi khối đến không phù hợp. Như Clifford đã chỉ ra, việc đệm trong một lớp I / O tệp sẽ không phải làm điều đó, nó sẽ chỉ xóa bộ đệm hiện tại và sử dụng lại nó. Vì vậy, đây phải là một giới hạn trên về chi phí đầu ra của bộ đệm. Và đó chính xác là những gì cần thiết để tạo bộ đệm trong bộ nhớ hoạt động.

Vậy tại sao stringbufchậm hơn 2,5 lần trên ideone và chậm hơn ít nhất 10 lần khi tôi kiểm tra nó? Nó không được sử dụng đa hình trong tiêu chuẩn vi mô đơn giản này, do đó không giải thích được.


24
Bạn đang viết một triệu ký tự một lần và tự hỏi tại sao nó chậm hơn so với việc sao chép vào bộ đệm được sắp xếp trước?
Anon.

20
@Anon: Tôi đang đệm bốn triệu byte bốn lần một lần, và vâng tôi đang tự hỏi tại sao điều đó lại chậm. Nếu std::ostringstreamkhông đủ thông minh để tăng kích thước bộ đệm theo cấp số nhân std::vector, thì đó là (A) ngu ngốc và (B) một cái gì đó mọi người nghĩ về hiệu suất I / O nên nghĩ về. Dù sao, bộ đệm được sử dụng lại, nó không được phân bổ lại mỗi lần. Và std::vectorcũng đang sử dụng một bộ đệm tăng trưởng năng động. Tôi đang cố gắng công bằng ở đây.
Ben Voigt

14
Nhiệm vụ nào bạn thực sự cố gắng để điểm chuẩn? Nếu bạn không sử dụng bất kỳ tính năng định dạng nào ostringstreamvà bạn muốn hiệu suất càng nhanh càng tốt thì bạn nên xem xét việc đi thẳng stringbuf. Các ostreamlớp được cho là liên kết chức năng định dạng nhận thức cục bộ với sự lựa chọn bộ đệm linh hoạt (tệp, chuỗi, v.v.) thông qua rdbuf()giao diện chức năng ảo của nó. Nếu bạn không thực hiện bất kỳ định dạng nào thì mức độ gián tiếp bổ sung đó chắc chắn sẽ trông đắt đỏ tương đối so với các phương pháp khác.
CB Bailey

5
+1 cho sự thật op. Chúng tôi đã nhận được đặt hàng hoặc tốc độ thăng bằng cách di chuyển từ ofstreamđể fprintfkhi xuất ra thông tin đăng nhập liên quan đến đôi. MSVC 2008 trên WinXPsp3. iostreams chỉ là con chó chậm.
KitsuneYMG

6
Dưới đây là một số thử nghiệm trên trang web của ủy ban: open-std.org/jtc1/sc22/wg21/docs/D_5.cpp
Johannes Schaub - litb

Câu trả lời:


49

Không trả lời chi tiết cụ thể cho câu hỏi của bạn nhiều như tiêu đề: Báo cáo kỹ thuật năm 2006 về Hiệu suất C ++ có một phần thú vị trên IOStreams (tr.68). Liên quan nhất đến câu hỏi của bạn nằm trong Phần 6.1.2 ("Tốc độ thực hiện"):

Do các khía cạnh nhất định của quá trình xử lý IOStream được phân phối trên nhiều khía cạnh, nên có vẻ như Tiêu chuẩn bắt buộc phải triển khai không hiệu quả. Nhưng đây không phải là trường hợp - bằng cách sử dụng một số hình thức tiền xử lý, phần lớn công việc có thể tránh được. Với một trình liên kết thông minh hơn một chút so với thường được sử dụng, có thể loại bỏ một số sự không hiệu quả này. Điều này được thảo luận trong §6.2.3 và §6.2.5.

Vì báo cáo được viết vào năm 2006, người ta sẽ hy vọng rằng nhiều khuyến nghị sẽ được đưa vào các trình biên dịch hiện tại, nhưng có lẽ đây không phải là trường hợp.

Như bạn đã đề cập, các khía cạnh có thể không có trong write()(nhưng tôi sẽ không cho rằng điều đó một cách mù quáng). Vậy tính năng này là gì? Chạy GProf trên ostringstreammã của bạn được biên dịch bằng GCC sẽ phân tích như sau:

  • 44,23% trong std::basic_streambuf<char>::xsputn(char const*, int)
  • 34,62% ​​trong std::ostream::write(char const*, int)
  • 12,50% trong main
  • 6,73% trong std::ostream::sentry::sentry(std::ostream&)
  • 0,96% trong std::string::_M_replace_safe(unsigned int, unsigned int, char const*, unsigned int)
  • 0,96% trong std::basic_ostringstream<char>::basic_ostringstream(std::_Ios_Openmode)
  • 0,00% trong std::fpos<int>::fpos(long long)

Vì vậy, phần lớn thời gian được dành cho xsputn, cuối cùng sẽ gọi std::copy()sau khi kiểm tra và cập nhật nhiều vị trí và bộ đệm con trỏ (có một cái nhìn c++\bits\streambuf.tccchi tiết).

Tôi cho rằng điều này là bạn đã tập trung vào tình huống xấu nhất. Tất cả các kiểm tra được thực hiện sẽ là một phần nhỏ trong tổng số công việc được thực hiện nếu bạn đang xử lý các khối dữ liệu hợp lý lớn. Nhưng mã của bạn đang dịch chuyển dữ liệu theo bốn byte cùng một lúc và phát sinh tất cả các chi phí phụ mỗi lần. Rõ ràng người ta sẽ tránh làm như vậy trong một tình huống thực tế - hãy xem xét mức phạt sẽ không đáng kể như thế nào nếu writeđược gọi trên một mảng 1m ints thay vì trên 1m lần trên một int. Và trong một tình huống thực tế, người ta sẽ thực sự đánh giá cao các tính năng quan trọng của IOStreams, cụ thể là thiết kế an toàn bộ nhớ và loại an toàn. Những lợi ích như vậy có giá và bạn đã viết một bài kiểm tra khiến các chi phí này chi phối thời gian thực hiện.


Âm thanh như thông tin tuyệt vời cho một câu hỏi trong tương lai về hiệu suất của việc chèn / trích xuất định dạng của iostream mà có lẽ tôi sẽ hỏi sớm. Nhưng tôi không tin có bất kỳ khía cạnh nào liên quan đến ostream::write().
Ben Voigt

4
+1 cho hồ sơ (đó là một máy Linux tôi đoán?). Tuy nhiên, tôi thực sự thêm bốn byte cùng một lúc (thực ra sizeof i, nhưng tất cả các trình biên dịch tôi đang thử nghiệm đều có 4 byte int). Và điều đó dường như không thực tế đối với tôi, bạn nghĩ kích thước khối nào trong mỗi cuộc gọi đến xsputnmã thông thường như thế nào stream << "VAR: " << var.x << ", " << var.y << endl;.
Ben Voigt

39
@beldaz: Ví dụ mã "điển hình" đó chỉ gọi xsputnnăm lần rất có thể nằm trong một vòng lặp ghi tệp 10 triệu dòng. Truyền dữ liệu cho iostreams trong các khối lớn ít hơn nhiều so với kịch bản thực tế so với mã điểm chuẩn của tôi. Tại sao tôi phải ghi vào luồng được đệm với số lượng cuộc gọi tối thiểu? Nếu tôi phải thực hiện bộ đệm của riêng mình, thì điểm của iostreams là gì? Và với dữ liệu nhị phân, tôi có tùy chọn để tự đệm nó, khi viết hàng triệu số vào tệp văn bản, tùy chọn hàng loạt không tồn tại, tôi PHẢI gọi operator <<cho từng số.
Ben Voigt

1
@beldaz: Người ta có thể ước tính khi I / O bắt đầu thống trị với một phép tính đơn giản. Với tốc độ ghi trung bình 90 MB / s, điển hình của các ổ đĩa cứng tiêu dùng hiện tại, việc xóa bộ đệm 4 MB mất <45ms (thông lượng, độ trễ là không quan trọng vì bộ đệm ghi OS). Nếu chạy vòng lặp bên trong mất nhiều thời gian hơn để lấp đầy bộ đệm, thì CPU sẽ là yếu tố giới hạn. Nếu vòng lặp bên trong chạy nhanh hơn, thì I / O sẽ là yếu tố giới hạn hoặc ít nhất là còn một chút thời gian CPU để thực hiện công việc thực sự.
Ben Voigt

5
Tất nhiên, điều đó không có nghĩa là sử dụng iostreams nhất thiết phải là một chương trình chậm. Nếu I / O là một phần rất nhỏ của chương trình, thì việc sử dụng thư viện I / O với hiệu suất kém sẽ không có nhiều tác động chung. Nhưng việc không được gọi thường xuyên đủ để vật chất không giống như hiệu năng tốt và trong các ứng dụng nặng I / O, nó có vấn đề.
Ben Voigt

27

Tôi khá thất vọng về những người dùng Visual Studio ngoài kia, những người thích có một gimme về điều này:

  • Trong triển khai Visual Studio ostream, sentryđối tượng (được yêu cầu theo tiêu chuẩn) bước vào phần quan trọng bảo vệ streambuf(không bắt buộc). Điều này dường như không phải là tùy chọn, vì vậy bạn phải trả chi phí đồng bộ hóa luồng ngay cả đối với luồng cục bộ được sử dụng bởi một luồng duy nhất, không cần đồng bộ hóa.

Điều này làm tổn thương mã sử dụng ostringstreamđể định dạng tin nhắn khá nghiêm trọng. Sử dụng stringbuftrực tiếp để tránh việc sử dụng sentry, nhưng các toán tử chèn được định dạng không thể làm việc trực tiếp trên streambufs. Đối với Visual C ++ 2010, phần quan trọng đang chậm lại ostringstream::writetheo hệ số ba so với stringbuf::sputncuộc gọi cơ bản .

Nhìn dữ liệu hồ sơ của beldaz trên newlib , có vẻ như rõ ràng rằng gcc sentrykhông làm bất cứ điều gì điên rồ như thế này. ostringstream::writetrong gcc chỉ mất khoảng 50% lâu hơn stringbuf::sputn, nhưng stringbufbản thân nó chậm hơn nhiều so với dưới VC ++. Và cả hai vẫn so sánh rất bất lợi với việc sử dụng vector<char>bộ đệm cho I / O, mặc dù không có cùng tỷ lệ như trong VC ++.


Thông tin này vẫn được cập nhật? Việc triển khai AFAIK, C ++ 11 được gửi cùng với GCC thực hiện khóa 'điên' này. Chắc chắn, VS2010 vẫn làm điều đó. Bất cứ ai cũng có thể làm rõ hành vi này và nếu 'không bắt buộc' vẫn giữ trong C ++ 11?
mloskot

2
@mloskot: Tôi thấy không có yêu cầu về an toàn luồng trên sentry... "Lớp sentry định nghĩa một lớp chịu trách nhiệm thực hiện các hoạt động tiền tố và hậu tố an toàn ngoại lệ." và một lưu ý "Trình xây dựng và hàm hủy của sentry cũng có thể thực hiện các hoạt động phụ thuộc vào việc thực hiện bổ sung." Người ta cũng có thể phỏng đoán theo nguyên tắc C ++ là "bạn không trả tiền cho những gì bạn không sử dụng" mà ủy ban C ++ sẽ không bao giờ chấp nhận yêu cầu lãng phí như vậy. Nhưng hãy hỏi một câu hỏi về an toàn chủ đề iostream.
Ben Voigt

8

Vấn đề bạn thấy là tất cả chi phí xung quanh mỗi cuộc gọi để viết (). Mỗi mức độ trừu tượng mà bạn thêm (char [] -> vector -> chuỗi -> Ostringstream) sẽ thêm một vài lệnh gọi / trả về chức năng khác và nếu bạn gọi nó là một triệu lần - cộng lại.

Tôi đã sửa đổi hai trong số các ví dụ trên ideone để viết mười ints cùng một lúc. Thời gian quay vòng từ 53 đến 6 ms (cải thiện gần 10 lần) trong khi vòng lặp char được cải thiện (3,7 đến 1,5) - hữu ích, nhưng chỉ bằng một hệ số hai.

Nếu bạn quan tâm đến hiệu suất thì bạn cần chọn công cụ phù hợp cho công việc. Ostringstream là hữu ích và linh hoạt, nhưng có một hình phạt cho việc sử dụng nó theo cách bạn đang cố gắng. char [] là công việc khó khăn hơn, nhưng hiệu suất đạt được có thể rất lớn (hãy nhớ rằng gcc có thể cũng sẽ ghi nhớ các memcpys cho bạn).

Nói tóm lại, quá trình tạo dòng không bị phá vỡ, nhưng bạn càng đến gần kim loại thì mã của bạn sẽ chạy càng nhanh. Trình biên dịch vẫn có lợi thế cho một số dân gian.


8
Điều gì ostringstream::write()phải làm mà vector::push_back()không làm? Nếu bất cứ điều gì, nó sẽ nhanh hơn vì nó đã trao một khối thay vì bốn yếu tố riêng lẻ. Nếu ostringstreamchậm hơn std::vectormà không cung cấp bất kỳ tính năng bổ sung nào, thì vâng tôi sẽ gọi nó bị hỏng.
Ben Voigt

1
@Ben Voigt: Ngược lại, vectơ của nó phải làm điều đó mà không cần phải làm điều đó làm cho vectơ hiệu quả hơn trong trường hợp này. Vector được đảm bảo là tiếp giáp trong bộ nhớ, trong khi đó thì không. Vector là một trong những lớp được thiết kế để biểu diễn, trong khi Ostringstream thì không.
Dragontamer5788

2
@Ben Voigt: Sử dụng stringbuftrực tiếp sẽ không loại bỏ tất cả các lệnh gọi hàm vì stringbufgiao diện chung bao gồm các hàm không ảo công khai trong lớp cơ sở, sau đó gửi đến hàm ảo được bảo vệ trong lớp dẫn xuất.
CB Bailey

2
@Charles: Trên bất kỳ trình biên dịch tử tế nào, vì lệnh gọi hàm công khai sẽ được đưa vào bối cảnh trong đó kiểu động được biết đến với trình biên dịch, nó có thể loại bỏ các lệnh gọi và thậm chí là nội tuyến.
Ben Voigt

6
@Roddy: Tôi nên nghĩ rằng đây là tất cả mã mẫu nội tuyến, hiển thị trong mọi đơn vị biên dịch. Nhưng tôi đoán rằng có thể thay đổi bằng cách thực hiện. Chắc chắn tôi sẽ mong đợi cuộc gọi đang thảo luận, công chúngsputn chức năng gọi là bảo vệ ảo xsputn, sẽ được nội tuyến. Ngay cả khi xsputnkhông được nội tuyến, trình biên dịch có thể, trong khi nội tuyến sputn, xác định xsputnghi đè chính xác cần thiết và tạo một cuộc gọi trực tiếp mà không cần thông qua vtable.
Ben Voigt

1

Để có hiệu suất tốt hơn, bạn phải hiểu cách các container bạn đang sử dụng làm việc. Trong ví dụ mảng char [] của bạn, mảng có kích thước yêu cầu được phân bổ trước. Trong ví dụ về vectơ và đường truyền của bạn, bạn đang buộc các đối tượng phải liên tục phân bổ và phân bổ lại và có thể sao chép dữ liệu nhiều lần khi đối tượng phát triển.

Với std :: vector, điều này được giải quyết dễ dàng bằng cách khởi tạo kích thước của vectơ đến kích thước cuối cùng như bạn đã làm mảng char; thay vào đó, bạn không công bằng làm tê liệt hiệu suất bằng cách thay đổi kích thước về không! Đó không phải là một so sánh công bằng.

Đối với việc tạo dòng, việc mở rộng không gian là không thể, tôi sẽ đề nghị rằng đó là một cách sử dụng không phù hợp. Lớp này có tiện ích lớn hơn nhiều so với một mảng char đơn giản, nhưng nếu bạn không cần tiện ích đó, thì đừng sử dụng nó, bởi vì bạn sẽ trả chi phí trong mọi trường hợp. Thay vào đó, nó nên được sử dụng cho những gì tốt cho việc định dạng dữ liệu thành một chuỗi. C ++ cung cấp một loạt các container và một Ostringstram là một trong số ít phù hợp nhất cho mục đích này.

Trong trường hợp vectơ và đường truyền mà bạn nhận được bảo vệ khỏi lỗi tràn bộ đệm, bạn không nhận được điều đó với một mảng char và sự bảo vệ đó không miễn phí.


1
Phân bổ dường như không phải là vấn đề đối với hệ thống xương. Anh ta chỉ tìm cách trở về số 0 cho các lần lặp lại tiếp theo. Không cắt ngắn. Ngoài ra tôi đã thử ostringstream.str.reserve(4000000)và nó không có sự khác biệt.
Roddy

Tôi nghĩ với ostringstream, bạn có thể "dự trữ" bằng cách chuyển vào một chuỗi giả, tức là: ostringstream str(string(1000000 * sizeof(int), '\0'));Với vector, resizekhông phân bổ bất kỳ khoảng trống nào, nó chỉ mở rộng nếu cần.
Nim

1
"vector .. bảo vệ khỏi tràn bộ đệm". Một quan niệm sai lầm phổ biến - vector[]toán tử thường KHÔNG được kiểm tra các lỗi giới hạn theo mặc định. vector.at()là tuy nhiên.
Roddy

2
vector<T>::resize(0)không thường phân bổ lại bộ nhớ
Niki Yoshiuchi

2
@Roddy: Không sử dụng operator[], nhưng push_back()(bằng cách này back_inserter), điều này chắc chắn KHÔNG kiểm tra tràn. Đã thêm một phiên bản khác không sử dụng push_back.
Ben Voigt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.