it-swarm.asia

استخدام scanf () في برامج C++ أسرع من استخدام cin؟

لا أعرف ما إذا كان هذا صحيحًا ، لكن عندما كنت أقرأ FAQ على أحد المشكلات التي توفر المواقع ، وجدت شيئًا يثير انتباهي:

تحقق من طرق الإدخال/الإخراج. في C++ ، استخدام cin و cout بطيء جدًا. استخدم هذه ، وسوف تضمن عدم القدرة على حل أي مشكلة مع كمية لا بأس بها من المدخلات أو المخرجات. استخدم printf و scanf بدلاً من ذلك.

هل يمكن لشخص توضيح ذلك؟ هل استخدام scanf () في برامج C++ أسرع من استخدام cin >> شيء ؟ إذا كانت الإجابة بنعم ، فهل هذه ممارسة جيدة لاستخدامها في برامج C++؟ اعتقدت أنه كان C محددة ، على الرغم من أنني أتعلم فقط C++ ...

106
zeroDivisible

إليك اختبار سريع لحالة بسيطة: برنامج لقراءة قائمة بالأرقام من الإدخال القياسي و XOR جميع الأرقام.

إصدار iostream:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

إصدار scanf:

#include <stdio.h>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (1 == scanf("%d", &x))
    parity ^= x;
  printf("%d\n", parity);

  return 0;
}

النتائج

باستخدام برنامج ثالث ، قمت بإنشاء ملف نصي يحتوي على 33280276 رقم عشوائي. أوقات التنفيذ هي:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

لا يبدو أن تغيير إعدادات تحسين برنامج التحويل البرمجي يغير النتائج كثيرًا على الإطلاق.

وبالتالي: هناك حقا فرق السرعة.


EDIT: المستخدم clyfish يشير أدناه أن اختلاف السرعة يرجع إلى حد كبير إلى وظائف I/O iostream التي تحافظ على التزامن مع وظائف C I/O. يمكننا إيقاف هذا عن طريق مكالمة إلى std::ios::sync_with_stdio(false);:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  std::ios::sync_with_stdio(false);

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

نتائج جديدة:

iostream version:                       21.9 seconds
scanf version:                           6.8 seconds
iostream with sync_with_stdio(false):    5.5 seconds

C++ iostream يفوز! اتضح أن هذا المزامنة الداخلية/التنظيف هو ما يبطئ عادة iostream i/o. إذا لم نقم بخلط stdio و iostream ، فيمكننا إيقاف تشغيله ، ثم iostream هو الأسرع.

الكود: https://Gist.github.com/3845568

194
nibot

http://www.quora.com/Is-cin-cout-slower-than-scanf-printf/answer/Aditya-Vishwakarma

يمكن أن يكون أداء cin/cout بطيئًا لأنهم بحاجة إلى الحفاظ على التزامن مع مكتبة C الأساسية. يعد هذا ضروريًا إذا كان سيتم استخدام كل من C IO و C++ IO.

ومع ذلك ، إذا كنت ستستخدم فقط C++ IO ، فببساطة استخدم السطر أدناه قبل أي عمليات IO.

std::ios::sync_with_stdio(false);

لمزيد من المعلومات حول هذا الموضوع ، انظر إلى مستندات libstdc ++ المقابلة .

60
clyfish

ربما يكون scanf أسرع إلى حد ما من استخدام التدفقات. على الرغم من أن التدفقات توفر كثيرًا من أمان النوع ، وليس من الضروري تحليل سلاسل التنسيق في وقت التشغيل ، إلا أنها تتمتع عادة بميزة عدم طلب تخصيصات ذاكرة زائدة (يعتمد هذا على برنامج التحويل البرمجي ووقت التشغيل). ومع ذلك ، ما لم يكن الأداء هو هدفك الوحيد وأنت في المسار الحرج ، فعليك حقًا أن تفضل الأساليب الأكثر أمانًا (الأبطأ).

يوجد مقال لذيذ جدًا كتبه Herb Sutter " The String Formers of Manor Farm " الذي يتناول الكثير من التفاصيل عن أداء منسقي الأوتار مثل sscanf و lexical_cast وأي نوع من الأشياء يجعلهم يركضون ببطء أو بسرعة. هذا نوع من التشابه ، ربما لنوع الأشياء التي قد تؤثر على الأداء بين النمط C IO ونمط C++. تميل الاختلاف الرئيسي مع التنسيقات إلى أمان النوع وعدد عمليات تخصيص الذاكرة.

42
1800 INFORMATION

لقد أمضيت للتو أمسية في العمل على مشكلة على UVa Online (Factovisors ، مشكلة مثيرة جدًا للاهتمام ، راجعها):

http://uva.onlinejudge.org/index.php؟option=com_onlinejudge&Itemid=8&category=35&page=show_problem&problem=1080

كنت أحصل على TLE (تم تجاوز الحد الزمني) على عمليات التقديم الخاصة بي. في هذه المشكلات المتعلقة بحل مواقع المحكمين على الإنترنت ، يوجد لديك حوالي 2-3 من الوقت المحدد للتعامل مع آلاف حالات الاختبار المحتمل استخدامها لتقييم حلك. بالنسبة للمشاكل الكثيرة الحسابية مثل هذه المشكلة ، يتم حساب كل ميكروثانية.

كنت أستخدم الخوارزمية المقترحة (اقرأ عنها في منتديات المناقشة للموقع) ، لكنني ما زلت أحصل على TLEs.

لقد قمت فقط بتغيير "cin >> n >> m" إلى "scanf ("٪ d٪ d "، & n ، & m)" وعدد قليل من "couts" الصغيرة إلى "printfs" ، وتحولت TLE إلى "Accepted"!

لذلك ، نعم ، يمكن أن تحدث فرقًا كبيرًا ، خاصةً عندما تكون الحدود الزمنية قصيرة.

16
Bogatyr

إذا كنت تهتم بالأداء وتنسيق السلسلة ، فقم بإلقاء نظرة على مكتبة Matthew Wilson's FastFormat .

تحرير - رابط لنشر accu في تلك المكتبة: http://accu.org/index.php/journals/1539

6
xtofl

نعم iostream أبطأ من cstdio.
نعم ، ربما لا ينبغي أن تستخدم cstdio إذا كنت تتطور في C++.
بعد قولي هذا ، هناك طرق أسرع للحصول على I/O من scanf إذا كنت لا تهتم بالتنسيق ، اكتب الأمان ، بلاه ، بلاه ، بلاه ...

على سبيل المثال ، هذا روتين مخصص للحصول على رقم من STDIN:

inline int get_number()
{
    int c;        
    int n = 0;

    while ((c = getchar_unlocked()) >= '0' && c <= '9')
    {
        // n = 10 * n + (c - '0');
        n = (n << 3) + ( n << 1 ) + c - '0';
    }
    return n;
}
2
pedro.lupin

هناك تطبيقات stdio ( libio ) والتي تنفذ FILE * كـ دفق C++ ، و fprintf كمحلل لتنسيق وقت التشغيل. لا تحتاج IOstreams إلى تحليل تنسيق وقت التشغيل ، كل هذا يتم في وقت الترجمة. لذلك ، مع تقاسم الخلفية ، فمن المعقول أن نتوقع أن iostreams أسرع في وقت التشغيل.

2
MSalters

تكمن المشكلة في أن cin لديه الكثير من الحملات الضمنية لأنه يمنحك طبقة تجريدية أعلى مكالمات scanf(). يجب ألا تستخدم scanf() over cin إذا كنت تكتب برنامج C++ لأن هذا مطلوب cin. إذا كنت تريد الأداء ، فربما لن تكتب I/O في C++ على أي حال.

1
dreamlax

يبدو أن عبارات cin و cout بشكل عام أبطأ من scanfprintf في C++ ، لكنهما في الواقع أسرع!

الشيء هو: في C++ ، كلما استخدمت cin و cout ، تتم عملية المزامنة بشكل افتراضي للتأكد من أنه إذا كنت تستخدم scanf و cin في البرنامج ، فهما يعملان متزامنين مع بعضهما البعض. تستغرق عملية المزامنة هذه وقتًا. وبالتالي cin و cout الظهور ليكون أبطأ.

ومع ذلك ، إذا تم تعيين عملية المزامنة على عدم حدوثها ، يكون cin أسرع من scanf.

لتخطي عملية المزامنة ، قم بتضمين مقتطف الشفرة التالي في البرنامج مباشرةً في بداية main():

std::ios::sync_with_stdio(false);

زيارة هذا الموقع لمزيد من المعلومات.

0
Prasoon Varshney
#include <stdio.h>
#include <unistd.h>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

static int scanuint(unsigned int* x)
{
  char c;
  *x = 0;

  do
  {
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while(c<'0' || c>'9');

  do
  {
      //*x = (*x<<3)+(*x<<1) + c - '0';
      *x = 10 * (*x) + c - '0';
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while ((c>='0' && c<='9'));

  return 0;
}

int main(int argc, char **argv) {

  int parity = 0;
  unsigned int x;

  while (1 != (scanuint(&x))) {
    parity ^= x;
  }
  parity ^=x;
  printf("%d\n", parity);

  return 0;
}

يوجد خطأ في نهاية الملف ، ولكن رمز C هذا أسرع بشكل كبير من إصدار C++ الأسرع.

[email protected] 3845568-78602a3f95902f3f3ac63b6beecaa9719e28a6d6 ▶ make test        
time ./xor-c < Rand.txt
360589110

real    0m11,336s
user    0m11,157s
sys 0m0,179s
time ./xor2-c < Rand.txt
360589110

real    0m2,104s
user    0m1,959s
sys 0m0,144s
time ./xor-cpp < Rand.txt
360589110

real    0m29,948s
user    0m29,809s
sys 0m0,140s
time ./xor-cpp-noflush < Rand.txt
360589110

real    0m7,604s
user    0m7,480s
sys 0m0,123s

استغرق C++ الأصلي 30sec استغرق C رمز 2sec.

0
hexec