Чому б коли-небудь можливо, щоб Java була швидшою, ніж C ++?


79

Іноді Java переважає C ++ у показниках. Звичайно, інколи C ++ перевершує.

Дивіться наступні посилання:

Але як це можливо навіть? Думає, що інтерпретований байт-код міг бути швидше, ніж мова, що складена.

Може хтось, будь ласка, пояснить? Дякую!


2
Ви можете подивитися на shootout.alioth.debian.org/u32/…, щоб побачити типи проблем, які швидше працюють на java / c ++ ... Дивіться схему проблем, а не ці конкретні проблеми ...
c0da

2
Дивіться, чому у java була репутація повільності? для багатьох деталей на цю тему.
Péter Török

11
Це суперечить закону (Розділ 10.101.04.2c) створювати Java VM, яка працює швидше, ніж виконуваний двійковий файл, створений за допомогою C ++.
Mateen Ulhaq

1
@muntoo Що ти маєш на увазі? Розділ 10.101.04.2с чого?
Хайленд Марк

3
@HighlandMark Ви не є учасником кола. Ви не повинні знати, що відбувається всередині кола. Коло є абсолютним - закони кола витісняють закони природи. Ви не можете не кидати викликів, ані сумніватися в колі. Я коло, а коло - це я.
Mateen Ulhaq

Відповіді:


108

По-перше, більшість JVM включають компілятор, тому "інтерпретований байт-код" насправді є досить рідкісним (принаймні, в бенчмарковому коді - це не так вже й рідко в реальному житті, де ваш код, як правило, більше кількох тривіальних циклів, які повторюються надзвичайно часто ).

По-друге, досить велика кількість задіяних орієнтирів здається досить упередженою (чи то наміром, чи некомпетентністю, я не можу сказати). Так, наприклад, років тому я переглянув частину вихідного коду, пов’язаного з одного із посилань, які ви опублікували. Він мав такий код:

  init0 = (int*)calloc(max_x,sizeof(int));
  init1 = (int*)calloc(max_x,sizeof(int));
  init2 = (int*)calloc(max_x,sizeof(int));
  for (x=0; x<max_x; x++) {
    init2[x] = 0;
    init1[x] = 0;
    init0[x] = 0;
  }

Оскільки callocзабезпечує пам'ять, яка вже занулена, використовувати forцикл до нуля, це, очевидно, марно. Після цього (якщо пам'ять слугує), заповнюючи пам'ять іншими даними в будь-якому випадку (і жодна залежність від того, щоб вона була нульовою), тому все нулювання все одно було абсолютно непотрібним. Заміна вищевказаного коду на просту malloc(як і будь-яка розумна людина, як би колись починала з) покращила швидкість версії C ++, достатня для перемоги над версією Java (з досить широким відривом, якщо використовується пам'ять).

Розгляньте (для іншого прикладу) methcallтест, використаний у записі блогу у вашому останньому посиланні. Незважаючи на назву (і як це може виглядати), версія C ++ взагалі не дуже сильно оцінює накладні виклики методів. Частина коду, яка виявляється критичною, знаходиться в класі Toggle:

class Toggle {
public:
    Toggle(bool start_state) : state(start_state) { }
    virtual ~Toggle() {  }
    bool value() {
        return(state);
    }
    virtual Toggle& activate() {
        state = !state;
        return(*this);
    }
    bool state;
};

Критична частина виявляється найвищою state = !state;. Розглянемо, що відбувається, коли ми змінимо код, щоб кодувати стан як intзамість bool:

class Toggle {
    enum names{ bfalse = -1, btrue = 1};
    const static names values[2];
    int state;

public:
    Toggle(bool start_state) : state(values[start_state]) 
    { }
    virtual ~Toggle() {  }
    bool value() {  return state==btrue;    }

    virtual Toggle& activate() {
        state = -state;
        return(*this);
    }
};

Ця незначна зміна покращує загальну швидкість приблизно на 5: 1 . Навіть незважаючи на те, що тест призначений для вимірювання часу виклику методу, насправді більшість того, що він вимірював, був час для перетворення між intі bool. Я, безумовно, погоджуюся, що неефективність, яку показує оригінал, прикро - але, враховуючи, як рідко це здається в реальному коді, і легкість, з якою це можна виправити, коли / якщо він виникає, мені важко замислитися це як багато що означає.

У випадку, якщо хтось вирішить повторно запустити базові показники, я також повинен додати, що існує майже однаково тривіальна модифікація версії Java, яка виробляє (або принаймні за один раз виробляється - я не повторно запускав тести з недавній JVM для підтвердження, що вони все ще роблять) досить істотне поліпшення і версії Java. Версія Java має NthToggle :: activate (), який виглядає приблизно так:

public Toggle activate() {
this.counter += 1;
if (this.counter >= this.count_max) {
    this.state = !this.state;
    this.counter = 0;
}
return(this);
}

Зміна цього на виклик базової функції замість маніпулювання this.stateбезпосередньо дає досить значне підвищення швидкості (хоча й недостатньо, щоб не відставати від модифікованої версії C ++).

Отже, що ми закінчуємо - помилкове припущення про інтерпретовані байтові коди порівняно з деякими найгіршими тестами (які я коли-небудь бачив). Це не дає значущого результату.

Мій власний досвід полягає в тому, що з однаково досвідченими програмістами, які приділяють однакову увагу оптимізації, C ++ буде бити Java частіше, ніж ні - але (принаймні між цими двома), мова рідко зробить стільки ж різниці, як програмісти та дизайн. Посилання, що цитуються, розповідають більше про (не) компетентність / (не) чесність їх авторів, ніж про мови, які вони хочуть орієнтувати.

[Редагувати: Як мається на увазі в одному місці вище, але ніколи не зазначалося так прямо, як я, мабуть, маю, результати, які я цитую, - це ті результати, які я отримав, коли тестував це ~ 5 років тому, використовуючи C ++ та Java-реалізації, які були поточними на той час . Я не повторював тести з поточними реалізаціями. Однак огляд вказує на те, що код не було виправлено, тому все, що було б змінено, - це можливість компілятора приховати проблеми в коді.]

Якщо ми будемо ігнорувати приклади Java, проте, це дійсно можливо інтерпретувати код , щоб працювати швидше , ніж скомпільований код (хоча важко і дещо незвично).

Звичайний спосіб цього відбувається, що інтерпретується код набагато компактніше, ніж машинний код, або він працює на центральному процесорі, який має більший кеш даних, ніж кеш-код.

У такому випадку невеликий інтерпретатор (наприклад, внутрішній інтерпретатор реалізації Forth) може мати можливість повністю вміститися в кеш-коді, а програма, яку він інтерпретує, повністю вписується в кеш даних. Кеш, як правило, швидший, ніж основна пам'ять, на коефіцієнт щонайменше 10, а часто і набагато більше (коефіцієнт 100 вже не особливо рідкісний).

Отже, якщо кеш швидше, ніж основна пам'ять, на коефіцієнт N, і для впровадження кожного байтового коду потрібно менше інструкцій машинного коду, байт-код повинен виграти (я спрощую, але, думаю, загальна ідея повинна все-таки бути очевидним).


26
+1, повний акк. Особливо "мова рідко матиме стільки різниці, як програмісти та дизайн" - ви часто натрапляєте на проблеми, коли можна оптимізувати алгоритм, наприклад, покращити big-O, який дасть набагато більше імпульсу, ніж найкращий компілятор міг би.
шнадер

1
"У випадку, якщо хтось вирішить повторно запустити відповідні орієнтири ..." НЕ! Ще в 2005 році ці старі завдання були відкинуті і замінені завданнями, показаними в грі з тестами. Якщо хтось хоче повторно запустити деякі програми, будь ласка, перезапустіть поточні програми для поточних завдань, показаних на домашній сторінці гри з бенчмарками shootout.alioth.debian.org
igouy

@igouy: Деякі люди можуть хотіти просто підтвердити / заперечити результати результатів, які вони застосовували, з мінімальними виправленнями, необхідними, щоб принаймні дати їм мінімальний зв'язок з реальністю. У той же час ви в основному праві: відповідні орієнтири настільки погані, що просто виправлення найбільш очевидних помилок не допоможе сильно.
Джеррі Труну

І саме тому, ще в 2005 році їх відкинули та замінили завдання, показані зараз у грі з орієнтирами. Люди, які краще не знають, перезапускають ці старі програми.
igouy

13
+1 Мені не подобається, що люди кодують C ++ або в стилі C, або Java, а потім заявляють, що Java є вищою. відмова від відповідальності: я не називаю жодної мови вищою, але написання хитрого коду C ++ у стилі, який може бути ідеально підходить до іншої мови, не робить обидві мови порівнянними.
Крістіан Рау

111

Ручний прокат C / C ++, зроблений експертом з необмеженим часом , буде як мінімум таким же швидким або швидшим, ніж Java. Зрештою, сама Java написана на C / C ++, тому ви, звичайно, можете робити все, що робить Java, якщо ви готові докласти досить інженерних зусиль.

На практиці, однак, Java часто виконується дуже швидко з наступних причин:

  • Компіляція JIT - хоча класи Java зберігаються у вигляді байт-коду, цей компілятор JIT (як правило) збирається у нативний код під час запуску програми. Після компіляції це чистий кодовий код - тому теоретично можна очікувати, що він буде виконувати так само добре, як і компільований C / C ++ після того, як програма працює досить довго (тобто після того, як буде зроблена вся компіляція JIT)
  • Збір сміття на Яві надзвичайно швидкий та ефективний - Hotspot GC - це, мабуть, найкраща всебічна реалізація GC у світі. Це результат багатьох людських років експертних зусиль Sun та інших компаній. Практично, будь-яка складна система управління пам’яттю, яку ви робите в C / C ++, буде гіршою. Звичайно, ви можете писати досить швидкі / легкі основні схеми управління базовою пам'яттю в C / C ++, але вони не будуть майже такими ж універсальними, як повноцінна система GC. Оскільки більшість сучасних систем потребують складного управління пам’яттю, тому Java має велику перевагу для реальних ситуацій.
  • Краще націлювання на платформу - затримуючи компіляцію до запуску програми (компіляція JIT тощо), компілятор Java може скористатися тим, що знає точний процесор, на якому виконується. Це може дозволити деякі дуже вигідні оптимізації, які ви не змогли б зробити в попередньо складеному коді C / C ++, який повинен орієнтуватися на набір інструкцій процесора "найнижчий загальний знаменник".
  • Статистика часу виконання - оскільки компіляція JIT проводиться під час виконання, вона може збирати статистику під час виконання програми, що забезпечує кращі оптимізації (наприклад, знаючи ймовірність прийняття певної гілки). Це може дозволити компіляторам Java JIT виробляти кращий код, ніж компілятори C / C ++ (які повинні заздалегідь "здогадатися" про найбільш вірогідну гілку; припущення, яке часто може бути помилковим).
  • Дуже хороші бібліотеки - час виконання Java містить безліч добре написаних бібліотек з хорошою продуктивністю (особливо для серверних додатків). Часто це краще, ніж ви могли написати самостійно або отримати легко для C / C ++.

У той же час C / C ++ також має деякі переваги:

  • Більше часу для вдосконалених оптимізацій - компіляція C / C ++ робиться один раз, і тому ви можете витратити чималий час на розширені оптимізації, якщо налаштувати її на це. Немає теоретичних причин, чому Java не могла зробити те ж саме, але на практиці ви хочете, щоб Java до JIT-компілював код порівняно швидко, тому компілятор JIT сфокусований на "простіших" оптимізаціях.
  • Інструкції, які не можна виразити в байт-коді - хоча байт-код Java є цілком загальним призначенням, все ж є деякі речі, які ви можете зробити на низькому рівні, що ви не можете зробити в байт-коді (неперевірена арифметика вказівника - хороший приклад!). Використовуючи такі хитрощі (ab), ви можете отримати деякі переваги у виконанні
  • Менше обмежень щодо безпеки - Java виконує додаткову роботу, щоб гарантувати безпеку та надійність програм. Прикладами є перевірка меж масивів, певні гарантії сумісності, перевірка нульових покажчиків, безпека набору типів і т. Д. Уникаючи цього в C / C ++, ви можете отримати певні результативність (хоча, мабуть, це може бути поганою ідеєю!)

Загалом:

  • Java та C / C ++ можуть досягти подібних швидкостей
  • C / C ++, ймовірно, має незначні переваги в екстремальних обставинах (не дивно, що розробники ігор AAA все ще віддають перевагу, наприклад)
  • На практиці це залежатиме від того, як різні фактори, перераховані вище, балансують для вашої конкретної програми.

9
Оголошення "більше часу на оптимізацію в C ++": Це одна з змін, яку робить Oracle VM, коли ви вибрали сервер VM: він приймає більш високу вартість запуску, щоб забезпечити більш високу продуктивність у довгостроковій перспективі. Клієнтський віртуальний комп'ютер, проте, налаштований на оптимальний час запуску. Тож ця відмінність існує навіть у Java.
Йоахім Зауер

8
-1: Компілятор C ++ може зайняти набагато більше часу (години, буквально, для великої бібліотеки), щоб створити дуже оптимізований бінарний файл. Компілятор Java JIT не може зайняти стільки часу, навіть "серверна" версія. Я серйозно сумніваюся, що компілятор Java JIT зможе виконувати оптимізацію всієї програми, як це робить компілятор MS C ++.
Quant_dev

20
@quant_dev: впевнений, але чи не це саме те, про що я говорив у своїй відповіді як перевагу C ++ (більше часу для вдосконаленої оптимізації)? Так чому -1?
mikera

13
Збір сміття не є перевагою швидкості для Java. Перевага в швидкості - це лише програміст на C ++, який не знає, що ти робиш. Якщо все, що ви перевіряєте, - як швидко ви можете виділити, то так, сміттєзбірник виграє. Однак загальну ефективність програми все ж можна покращити, керуючи пам'яттю вручну.
Біллі ONeal

4
... Але за допомогою C ++ ви завжди можете теоретично поставити "схожий на JIT шар", який робить подібні оптимізації гілок під час виконання, зберігаючи швидкість програми C ++. (Теоретично. :()
Матін Ульхак

19

Виконання Java не інтерпретує байт-код. Швидше, він використовує те, що називається Just In Time Compilation . По суті, під час запуску програми він бере байт-код і перетворює його в нативний код, оптимізований для конкретного процесора.


На практиці так. В принципі, це залежить - на ранніх віртуальних машинах Java використовуються інтерпретатори байт-кодів, і, ймовірно, ви все ще можете знайти VM, що інтерпретують байт-код, якщо ви виглядаєте досить важко.
Стів314,

10
@ Steve314: але суто інтерпретуючі VM не будуть тими, що перевершують C ++, тому вони не дуже важливі для цього питання.
Йоахім Зауер

Компілятор JIT може також динамічно оптимізувати для конкретного використання коду, що неможливо з кодом, який складено статично.
starblue

2
@starblue, ну, це можливо дещо зі статичною компіляцією - див. оптимізацію, керовану профілем.
SK-логіка

18

За рівних умов можна сказати: ні, Java ніколи не повинна бути швидшою . Ви завжди можете реалізувати Java в C ++ з нуля і тим самим отримати як мінімум гарну ефективність. На практиці, однак:

  • JIT компілює код на машині кінцевого користувача, що дозволяє оптимізувати його для точного ЦП, який вони працюють. Хоча тут є компіляція для компіляції, вона може окупитися за інтенсивні програми. Часто програми реального життя не компілюються для використовуваного процесора.
  • Компілятор Java може бути краще в автоматичній оптимізації речей, ніж компілятор C ++. Або це не може, але в реальному світі речі не завжди є ідеальними.
  • Поведінка ефективності може відрізнятися внаслідок інших факторів, таких як збирання сміття. У C ++ ви зазвичай викликаєте деструктора негайно, коли закінчите з об'єктом. У Java ви просто випускаєте посилання, затримуючи фактичне знищення. Це ще один приклад різниці, якої немає ні тут, ні в плані продуктивності. Звичайно, ви можете стверджувати, що ви могли реалізувати GC в C ++ і зробити це з ним, але реальність така, що мало хто робить / хоче / може.

Як осторонь, це нагадує мені дискусію щодо С у 80-х / 90-х. Всім було цікаво, "чи може коли-небудь бути так швидко, як збірка?". В основному, відповідь була: ні на папері, але насправді компілятор C створив ефективніший код, ніж 90% програмістів для складання (ну, як тільки він трохи дозрів).


2
Що стосується GC, то не лише GC може затримати знищення об'єктів (що не має значення в більш тривалому періоді); Справа в тому, що в сучасних ГК розподіл / делокація короткотривалих об’єктів на Яві надзвичайно дешева в порівнянні з C ++.
Péter Török

@ PéterTörök так, ви праві, хороший пункт.
Даніель Б

9
@ PéterTörök Але в C ++ на стек часто ставлять недовговічні об'єкти, що в свою чергу набагато швидше, ніж будь-яка купа GC-ed, яку може використовувати Java.
Quant_dev

@quant_dev, ви забули ще один важливий ефект GC: ущільнення. Тож я не був би настільки впевнений, який шлях швидший.
SK-логіка

3
@DonalFellows Чому ви вважаєте, що я повинен турбуватися про управління пам'яттю в C ++? Більшість часу я цього не роблю. Існують прості шаблони, які потрібно застосувати, які відрізняються від Java, але це все.
Quant_dev

10

Але розподіл - це лише половина управління пам'яттю - делокація - інша половина. Виявляється, для більшості об’єктів вартість прямого вивезення сміття становить - нуль. Це відбувається тому, що колектору копіювання не потрібно відвідувати чи копіювати мертві об’єкти, а лише живі. Тож об’єкти, які стають сміттям незабаром після виділення, не вносять навантаження в цикл збору.

...

JVM на диво добре розбирати речі, про які ми припускали, що тільки розробник міг знати. Дозволяючи JVM вибирати між розподілом стеків та розподілом купи у кожному конкретному випадку, ми можемо отримати переваги від продуктивності розподілу стеків, не змушуючи програміста агонізувати, чи слід виділяти на стек чи на купу.

http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html


Це лише невелика частина всієї картини, але все-таки досить актуальна.
Йоахім Зауер

2
Мені подобається, як це суть: java для нообів, довіряй магічному GC, він знає краще.
Морг.

1
@Morg: Або ви можете прочитати це так: Java призначена для людей, які люблять заробляти справи, а не витрачати свій час на подвійне подвійне чи ручне управління пам’яттю.
Ландей

4
@Landei Я думаю, що ваш коментар матиме набагато більшу довіру, якби будь-яка пристойна широко використовувана база даних коду була написана на Java. У моєму світі справжні ОС записуються на C, postgreSQL написано на C, як і найважливіші інструменти, які справді будуть боляче переписувати. Java (і це навіть офіційна версія) дозволила менш кваліфікованим людям програмувати в стадах і в той же час досягти відчутних результатів.
Морг.

1
@Morg Мені здається дуже дивним, як ти, здається, фокусується саме на ОС. Це просто не може бути хорошим заходом з кількох причин. По-перше, вимоги до ОС кардинально відрізняються від більшості інших програм, по-друге, у вас є принцип Panda thumb (хто хоче переписати повну ОС на іншій мові, хто хоче написати власну ОС, якщо є робочі та навіть безкоштовні альтернативи?) і третє інше програмне забезпечення використовує функції ОС, тому не потрібно ніколи писати драйвер диска, диспетчер завдань і т. д. Якщо ви не можете надати кращі аргументи (не базуючись повністю на ОС), ви звучите як ненависник.
Ландей

5

Хоча повністю оптимізована програма Java рідко перемагає повністю оптимізовану програму C ++, розбіжності в таких речах, як управління пам’яттю, можуть зробити багато алгоритмів ідіоматично реалізованих на Java швидше, ніж ті ж алгоритми, які ідіоматично реалізовані в C ++.

Як зазначав @Jerry Coffin, є чимало випадків, коли прості зміни можуть зробити код набагато швидшим, але часто це може зайняти занадто багато нечистого перетворення однією або іншою мовою, щоб покращити ефективність. Це, мабуть, те, що ви побачили в хорошому орієнтирі, який показує, що Java працює краще, ніж C ++.

Крім того, хоча зазвичай це не все так важливо, є певна оптимізація продуктивності того, що мова JIT, як Java, може зробити це, що C ++ не може. Час виконання Java може включати в себе вдосконалення після складання коду, що означає, що JIT може потенційно створювати оптимізований код, щоб скористатися новими (або принаймні іншими) функціями процесора. З цієї причини 10-річний двійковий файл Java може потенційно перевершити 10-річний двійковий файл C ++.

Нарешті, повна безпека типу на великих зображеннях у дуже рідкісних випадках може запропонувати надзвичайні покращення продуктивності. Сингулярність , експериментальна ОС, написана майже повністю мовою на базі C #, має набагато швидший міжпроцесорний зв’язок та багатозадачність через те, що немає необхідності в апаратних межах процесу або дорогих контекстних комутаторах.


5

Опублікував Тім Холлоуей на JavaRanch:

Ось примітивний приклад: Назад, коли машини працювали в математично визначених циклах, галузева інструкція зазвичай мала 2 різні таймінги. Один для того, коли взяли гілку, один для того, коли гілку не брали. Зазвичай випадок без філій був швидшим. Очевидно, це означало, що ви можете оптимізувати логіку, базуючись на знанні, який випадок є більш поширеним (за умови обмеження, що те, що ми "знаємо", не завжди є тим, що насправді має місце).

Перекомпіляція JIT робить цей крок далі. Він контролює фактичне використання в режимі реального часу та відтворює логіку, виходячи з того, що насправді є найбільш поширеним випадком. І поверніть її знову, якщо зміна навантаження. Статично складений код не може цього зробити. Ось чому Java може іноді виконувати вручну налаштовану збірку / код C / C ++.

Джерело: http://www.coderanch.com/t/547458/Performance/java/Ahead-Time-vs-Just-time


3
І ще раз це неправильно / неповно. Статичні компілятори з керованою профілем оптимізацією можуть це визнати.
Конрад Рудольф

2
Конраде, статичні компілятори можуть перевернути логіку на основі поточного навантаження? Як я розумію, статичні компілятори генерують код один раз, і він залишається таким же назавжди.
Тіаго Негрі

2
Поточне навантаження, немає. Але типовий навантаження. Профільована оптимізація аналізує, як ваша програма працює під типовим навантаженням і відповідно оптимізує гарячі точки, як і HotSpot JIT.
Конрад Рудольф

4

Це тому, що завершальний крок генерування машинного коду відбувається прозоро всередині JVM під час запуску вашої програми Java, а не явного під час створення вашої програми C ++.

Вам слід врахувати той факт, що сучасний JVM витрачає досить багато часу на компіляцію байтового коду в польоті до рідного машинного коду, щоб зробити його максимально швидким. Це дозволяє JVM робити всі види хитрощів компілятора, що може бути ще краще, знаючи дані профілювання запущеної програми.

Якраз така річ, як автоматичне вбудовування геттера, так що JUMP-RETURN не потрібен, щоб просто отримати значення, прискорює роботу.

Однак те, що дійсно дозволило швидкі програми, - це краще прибирання після цього. Механізм вивезення сміття на Java швидший, ніж ручний без малок у C. Багато сучасних реалізацій, які не вимагають малалока, використовують підбирач сміття під ним.


Зауважте, що цей вбудований матеріал робить запуск JVM більшим і повільнішим, поки кращий код не має шансів наздогнати.

1
"У багатьох сучасних реалізаціях, що не містять малалоків, використовується сміттєзбірник". Дійсно? Я хотів би знати більше; У вас є посилання?
Шон Макміллан

Дякую. Я намагався знайти спосіб сказати, що JVM більше не містить просто своєчасний компілятор, який збирається у виконуваний код, але компілятор гарячих точок, який профілює запущений код і в подальшому оптимізує його. Я одноразовий компілятор, як C ++, бореться з цим.
Хайленд Марк

@SeanMcMillan деякий час я бачив аналіз щодо того, наскільки ефективними є безмелкові реалізації, де згадувалося, що найшвидший під ним використовував сміттєзбірник. Я не можу згадати, де я це читав.

Це був консервативний GC BDW?
Демі

4

Коротка відповідь - це не так. Забудьте про це, тема стара, як вогонь чи колесо. Java або .NET не є і не буде швидшим, ніж C / C ++. Це досить швидко для більшості завдань, де взагалі не потрібно думати про оптимізацію. Як форми та SQL-обробка, але на цьому вона закінчується.

Для тестів або невеликих додатків, написаних некомпетентними розробниками, так, кінцевим результатом буде те, що Java / .NET, ймовірно, буде близьким і, можливо, ще швидшим.

Насправді, прості речі, такі як розподіл пам’яті на стек або просто використання мемзонів, просто вб'ють Java / .NET на місці.

Світ зібраного сміття використовує своєрідну мемзону з усіма бухгалтерськими обліку. Додавання мемзони до C і C буде швидше там, на місці. Спеціально для тих показників Java "C", які відповідають "високоефективним кодам":

for(...)
{
alloc_memory//Allocating heap in a loop is verrry good, in't it?
zero_memory//Extra zeroing, we really need it in our performance code
do_stuff//something like memory[i]++
realloc//This is lovely speedup
strlen//loop through all memory, because storing string length is soo getting old
free//Java will do that outside out timing loop, but oh well, we're comparing apples to oranges here
}//loop 100000 times

Спробуйте використовувати змінні на основі стека в C / C ++ (або розміщення нове), вони перекладаються на sub esp, 0xff, це єдина інструкція x86, перемогти яку з Java - ви не можете ...

Більшу частину часу я бачу ті лавки, де порівнюється Java з C ++, це змушує мене йти як, wth? Неправильні стратегії розподілу пам'яті, самостійно зростаючі контейнери без резервів, кілька нових. Це навіть не близький до продуктивності код C / C ++.

Також добре прочитайте: https://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf


1
Неправильно. Цілком неправильно. Ви не зможете перевершити компактний GC з вашим ручним управлінням пам'яттю. Наївний підрахунок посилань ніколи не буде кращим, ніж власне рубеж. Як тільки мова заходить про складне управління пам’яттю, C ++ стає ретардом.
SK-логіка

3
@ SK-Logic: Неправильно, з мембранами або розподілом стека немає ВСІХ розподілу пам'яті чи операції розміщення. У вас є блок пам'яті, і ви просто пишете на нього. Позначте блок як вільний з летючою змінною на зразок захисту паралельної валюти InterlockedExchange і т. Д., А наступний потік просто скидає свої дані в попередньо виділений блок, не відвідуючи ОС для пам'яті взагалі, якщо він бачить, що це безкоштовно. З стеком це ще простіше, за єдиним винятком, що ви не можете скинути 50MB на стек. І термін експлуатації цього об’єкта знаходиться лише всередині {}.
Кодер

2
@ SK-логіка: Компілятори - це правильність по-перше, продуктивність по-друге. Пошукові системи, бази даних, системи торгівлі в режимі реального часу, ігри - це те, що я вважаю ефективними. І більшість з них покладаються на плоскі структури. І в будь-якому випадку компілятори в основному пишуться на C / C ++. З певними розподільниками я здогадуюсь. Потім я знову не бачу проблем із використанням елементів дерева або списку через рамну карту. Ви просто використовуєте нове місце розташування. У цьому немає великої складності.
Кодер

3
@ SK-логіка: Це не набагато швидше, кожен додаток .NET / Java, який я бачив, завжди виявлявся повільнішим і справжнім свином. Кожне перезапис керованого додатку в код SANE C / C ++ призводило до більш чистого та легшого застосування. Керовані програми завжди важкі. Див. VS2010 проти 2008 року. Ті ж структури даних, але VS2010 - це HOG. Правильно написані програми C / C ++ зазвичай завантажуються в мілісекундах і не застрягають на екранах сплеску, а також витрачають набагато менше пам’яті. Єдиним недоліком є ​​те, що вам доводиться кодувати апаратно на увазі, і багато людей не знають, як це зараз. Це лише орієнтири, де керовані мають шанс.
Кодер

2
ваші анекдотичні докази не враховуються. Правильні орієнтири показують реальну різницю. Особливо дивно, що ви посилаєтесь на додатки GUI, прив’язані до об'ємних і неоптимальних бібліотек GUI. І, що важливіше - теоретично межа належної продуктивності набагато вищий для належно реалізованої GC.
SK-логіка

2

Реальність полягає в тому, що вони обидва просто асемблери високого рівня, які роблять саме те, що програміст каже їм, конкретно, як програміст повідомляє їм у тому порядку, який їм каже програміст. Різниці у виконанні настільки малі, що не мають значення для всіх практичних цілей.

Мова не "повільна", програміст написав повільну програму. Дуже рідко програма, написана найкращим чином на одній мові, перевершує (з будь-якою практичною метою) програму, яка робить те саме, використовуючи найкращий спосіб альтернативної мови, якщо тільки автор дослідження не зможе подрібнити свою конкретну сокиру.

Очевидно, що якщо ви збираєтеся в рідкісних випадках, таких як вбудовані системи в режимі реального часу, вибір мови може змінитися, але як часто це відбувається? і з тих випадків, як часто правильний вибір не є наочно очевидним.


2
Теоретично "ідеальний" JITting VM повинен перевершити статично складений код, пристосувавши свої оптимізації до динамічно зібраної інформації профілювання. На практиці компілятори JIT ще не такі розумні, але вони, принаймні, здатні створювати код подібної якості, як і їх більші та повільні статичні аналоги.
SK-логіка

2

Дивіться наступні посилання ... Але як це взагалі можливо? Думає, що інтерпретований байт-код міг бути швидше, ніж мова, що складена.

  1. Чи пропонують ці дописи в блозі достовірні докази?
  2. Чи дають ці публікації в блогах остаточні докази?
  3. Чи містять ці записи в блозі навіть докази щодо "інтерпретованого байт-коду"?

Кіт Леа каже вам, що є "очевидні недоліки", але нічого не робить щодо цих "очевидних вад". Ще в 2005 році ці старі завдання були відкинуті і замінені завданнями, показаними в грі з тестами .

Кіт Леа каже вам, що "взяв контрольний код для C ++ та Java з застарілої програми Great Shootout Great Computer and Run Test", але насправді він показує лише 14 з 25 цих застарілих тестів .

Кіт Леа зараз каже вам, що сім років тому не намагався довести що-небудь за допомогою допису в блозі, але тоді він сказав: "Мені було нудно, коли люди чують, що Java говорить повільно, коли я знаю, що це досить швидко ..." тоді він щось намагався довести.

Крістіан Фельде каже вам: "Я не створив код, просто перезапустив тести". як ніби це звільнило його від будь-якої відповідальності за його рішення щодо оприлюднення вимірювань завдань і програм, які вибрав Кіт Лі.

Чи дають вимірювання навіть 25 крихітних крихітних програм остаточне підтвердження?

Ці вимірювання призначені для програм, що працюють як "змішаний режим", Java не інтерпретується Java - "Запам'ятайте, як працює HotSpot". Ви можете легко дізнатися, наскільки добре працює Java "інтерпретований байт-код", тому що ви можете змусити Java лише інтерпретувати байт-код - просто час, коли деякі програми Java запускаються з і без опції -Xint.


-1

Мене дивує, наскільки поширене це дивне поняття "інтерпретований байт-код". Ви коли-небудь чули про компіляцію JIT? Ваш аргумент не можна застосувати до Java.

Але, залишаючи JVM убік, бувають випадки, коли прямий потіковий код або навіть тривіальна інтерпретація байт-коду може легко перевершити сильно оптимізований власний код. Пояснення досить просте: байт-код може бути досить компактним і відповідати вашому крихітному кешу, коли в рідній версії коду того ж алгоритму в кінці буде кілька пропусків кешу за одну ітерацію.


Поширювана інтерпретація, можливо, пояснюється людьми, розбираються в їхній інформатиці. Віртуальна машина java - це машина, яка приймає байт-код java та працює на машині / не / здатна виконувати власне байт-код Java, не маючи змоги написати функціонально еквівалентну нативну програму. Ерго - це перекладач. Ви можете дати своїм методам кешування будь-яке ім'я, яке ви можете придумати, JIT або іншим способом, але це тлумач за визначенням інтерпретатора.
титон

@thiton, швидше за все, твій власний CS-fu начебто слабкий. JVM не робить ніякої інтерпретації (для гарячих точок) - як хтось, хто наважиться згадати CS, ви повинні знати, що це означає, і чим оперативна семантика інтерпретації відрізняється від використання нативного коду. Але, ймовірно, ви просто не знаєте достатньо CS, щоб відрізнити компіляцію від інтерпретації.
SK-логіка

2
Гм, але байтовий код, щоб його запустити, повинен бути перетворений в нативний код - ви не можете подавати байт-код Java в центральний процесор. Отже аргумент розміру недійсний.
Quant_dev

@quant_dev, звичайно - я сказав, що ця справа абсолютно не пов'язана з JVM. Для роботи цього ефекту вам знадобиться набагато простіший байт-код.
SK-логіка

-1

JIT, GC і так далі, C ++ може бути дуже, дуже легко зробити набагато повільнішим, ніж Java. Це не відображатиметься в орієнтирах, але те саме додаток, яке написали розробник Java та розробник C ++, може бути набагато швидшим на Java.

  • Перевантаження оператора. Кожен простий оператор на зразок "+" або "=" може викликати сотні рядків коду, виконуючи перевірки безпеки, операції на диску, ведення журналів, відстеження та профілювання. І вони настільки прості у використанні, що як тільки ви перевантажуєте операторів, ви використовуєте їх природним чином і рясно, не помічаючи, як користування накопичується.
  • Шаблони. Вони не впливають на швидкість так само, як пам'ять. Необережне використання шаблонів призведе до генерування мільйонів рядків коду (альтернативи для основного шаблону) без того, щоб ви їх ніколи помічали. Але потім бінарне час завантаження, використання пам’яті, використання своп - все, що діє і проти орієнтирів. І використання оперативної пам’яті йде через дах.

Що стосується розширених моделей успадкування, то вони в значній мірі схожі - у C ++ є такі, яких у Java немає, і навпаки, але всі вони також мають подібні, значні витрати. Так що особливих переваг на C ++ у об'єктно-важкому програмуванні немає.

Ще один застереження: GC може бути швидшим або повільнішим, ніж управління розподілами вручну. Якщо ви виділяєте безліч дрібних об'єктів, в середовищі GC зазвичай виділяється шматок пам'яті, а шматки відправляються по мірі необхідності для нових об'єктів. У керованому - кожен об'єкт = окремий розподіл потребує значного часу. ОТОХ, якщо ви малок () багато пам’яті одночасно, а потім просто призначити фрагменти його вручну або використовувати кілька великих екземплярів об'єктів, ви можете підійти набагато швидше.


4
Я не погоджуюся з обома пунктами. Чи не використовуєте ви операторів чи методи, не має значення. Ви кажете, що вони поширяться. Дурниці - не більше ніж методи; вам або потрібно зателефонувати їм, чи ні. І шаблони призводять до не більше коду, ніж повторне написання цього конкретного коду за багаторазового використання. Коду може бути більше, ніж для диспетчеризації виконання (віртуальних функцій), але це теж буде неактуально: виконання кеш-рядків інструкцій має значення найбільш у вузьких циклах, і тут буде використано лише один шаблон інстанції шаблону, тому немає відповідного тиску пам'яті завдяки шаблонам.
Конрад Рудольф

Звичайний спосіб мислення полягає в тому, що методи дорогі, оператори - дешеві. Ви використовуєте методи, коли потрібно, оператори, коли ви хочете заощадити час та оптимізувати. Це не технічна справа, а психологічна - це не те, що оператори «важчі», вони просто набагато простіші у використанні та використовуються набагато частіше. (плюс ви можете перевантажувати часто використовуваного оператора попередньо існуючим кодом, роблячи це так само, як оригінал, плюс додатковий - і раптом весь код значно сповільниться.
SF.

Я заперечую, що цей психологічний факт є реальним, і навіть якщо він є, у вас немає вибору : якщо вам потрібна функціональність, ви використовуєте її, будь то інкапсульована в оператор чи метод. Психологія не має значення для вашого вибору семантики.
Конрад Рудольф

1
Хитрість питання. Я б взагалі не здогадувався про це взагалі, я б міряв, діяв тоді . У мене ніколи не було проблем з такою тактикою.
Конрад Рудольф

1
@KonradRudolph: Це все правда, коли мова йде про ясність та простоту написання коду, що робить його без помилок та ремонтом. Суть щодо ефективності реалізації алгоритму все ще існує: якщо ви збираєтесь написати obj.fetchFromDatabase("key")три рази протягом п’яти рядків коду для одного і того ж ключа, ви двічі подумаєте, чи отримати це значення один раз і зберегти його в локальній змінній. Якщо ви пишете obj->"key"з ->перевантажено , щоб діяти в якості вибірки бази даних, ви набагато більш схильні просто дозволити їй пройти , тому що вартість операції НЕ є очевидною.
СФ.

-2

Якимось чином стек-бірж не приймає інших моїх стек-пойнтів, тому ... жодної відповіді на жаль ...

Однак друга моя найбільш голосована відповідь тут повна дезінформації на мою скромну думку.

Вручну прокатний додаток експертом з C / C ++ ВЖЕ завжди буде набагато швидшим, ніж програма Java, період. Існує не так швидко, як Java або Швидше. це просто швидше, саме через пункти, які ви цитуєте нижче:

Компіляція JIT : Ви дійсно очікуєте, що автоматичний оптимізатор матиме розумні позначення експерта-програміста та бачить зв’язок між наміром та кодом, який CPU дійсно запустить ??? Крім того, весь JIT, який ви робите, втрачає час порівняно з уже складеною програмою.

Збір сміття - це інструмент, який просто роздає ресурси, які програміст забув би розібрати, більш-менш ефективно.

Очевидно, що це може бути лише повільніше, ніж те, що експерт (ви вибрали термін) програміст C зробив би для роботи зі своєю пам'яттю (і немає ніяких витоків у правильно написаних додатках).

Додаток, оптимізований під ефективність C, знає, на якому процесорі він працює, він складений на ньому, інакше це означає, що ви не зовсім зробили всі кроки для продуктивності?

Статистика часу виконання Це поза моїми знаннями, але я підозрюю, що експерт C має більш ніж достатньо знань для прогнозування галузей, щоб знову перевершити автоматизовану оптимізацію -

Дуже хороші бібліотеки Існує багато не дуже оптимізованих функцій, доступних через бібліотеки на Java, і те саме стосується будь-якої мови, проте найбільш оптимізовані бібліотеки записуються на С, особливо для обчислення.

JVM - це шар абстракції, який передбачає як хороші речі, багато з яких є вище, а також передбачає, що загальне рішення є більш повільним за конструкцією.

Загалом:

Java ніколи не може досягти швидкості C / C ++ через те, як вона працює в JVM з великою кількістю захисту, функцій та інструментів.

C ++ має чітку перевагу в оптимізованому програмному забезпеченні, будь то для обчислень або ігор, і звичайно бачити, що реалізація C ++ виграє конкурси кодування, до того, що найкращі реалізації Java можна побачити лише на другій сторінці.

На практиці C ++ не є іграшкою і не дозволить вам уникнути багатьох помилок, з якими може впоратися більшість сучасних мов, проте, будучи простішими та менш безпечними, це по суті швидше.

І як висновок, я хотів би сказати, що більшість людей не дають двох центів з цього приводу, що врешті-решт оптимізація - це спорт, зарезервований лише для небагатьох щасливих розробників і що, за винятком випадків, коли продуктивність справді викликає занепокоєння. (тобто, коли множення апаратного забезпечення на 10 не допоможе вам - або становить хоча б кілька мільйонів), більшість менеджерів віддадуть перевагу неоптимізованому додатку та тоні обладнання.


Знову. Збір сміття - це не лише "інструмент, який розмовляє". GC може ущільнити ваші структури. GC може вирішити ваші слабкі посилання та допомогти вам збалансувати кешування таким чином. Багатоступеневий GC дозволяє виділити купу набагато дешевше, ніж ваш об'ємний, повільний newабо malloc(). Загалом це може бути набагато швидше, ніж будь-яке управління пам'яттю вручну - оскільки ви не зможете перемістити об'єкти вручну. Отже, всі ваші міркування явно неправильні та упереджені. Ваші знання алгоритмів GC та методів оптимізації JIT занадто обмежені.
SK-логіка

4
Ця відповідь сповнена помилок щодо того, що можуть робити сучасні оптимізатори. Оптимізований вручну код не має шансів проти цього. Але тоді, C ++ також має оптимізуючий компілятор.
Конрад Рудольф

Дякуємо за ваш коментар SK-логіка, але, як ви заявляєте, GC може бути набагато швидшим в цілому, ми говоримо про те, що буде найшвидшим у конкретному випадку, і, здається, більшість людей згодні, що все, що може зробити GC зробити програміст може і навіть краще. Звичайно, ви можете перемістити об'єкти вручну, коли у вас є прямий доступ до пам'яті .. lol. Мої знання про внутрішні програми JVM, безумовно, обмежені, і я очікую, що Java-голови покажуть мені світло, а не просто скажуть мені випадкові лайно про те, що GC може робити речі, які не можна робити вручну (хай ... навіть GC повинен користуйтеся інструкціями CPU;)).
Морг.

Конрад, я згоден, я значною мірою недооцінюю сучасні оптимізатори ... однак мені цікаво, що ви розглядаєте оптимізований вручну код як неповноцінний коду, що оптимізується автоматично. Що саме ви очікуєте, що компілятор побачить, що людина не може?
Морг.

1
Правильно. продовжуйте натискати -1, це не змінить факту, що C ++ швидше, ніж Java. Я, можливо, не знаю багато про сучасні компілятори, але це не має жодного значення для основного моменту, що є правильним і суперечить найбільш голосовій відповіді тут. Чому б інакше C ++ був пріоритетом для nVidia на їх GPU для HPC. Чому б інакше всі ігри були написані на C ++, чому б інакше кожен двигун БД був написаний на C?
Морг.

-4

Я бачив щонайменше два вражаючі mmo, зроблені на Java, сказати, що його недостатньо швидко для ігор є помилковим. Тільки тому, що дизайнери ігор віддають перевагу C ++ більше, ніж інші мови, вони говорять, що це просто не лише Java, а це означає, що програмісти ніколи насправді не мали проблеми з будь-якими іншими мовами / парадигмами програмування. Що-небудь на будь-якій мові, такої розширеної як C / C ++ або навіть Java, може створювати код, який технічно може відповідати або перемогти аргумент швидкості. Все це добре і сказане зводиться до того, що знають програмісти, з якими командами працює найбільше і найголовніше, чому вони використовують зазначені інструменти. Оскільки ми вирішуємо аспект програмування розвитку ігор, то до аргументу повинно бути більше. Простіше кажучи " Про все, що стосується грошей і часу для бізнес-мертвих, використання інструментів, які відповідають QA, а в реальному світі не має жодної ваги з xx причин вибору C ++ над Java або будь-якою іншою мовою. Це просто рішення про масове виробництво. На самому базовому рівні обчислювальних алгоритмів, з якими ми граємо, - це одиниці та нулі, аргумент швидкості - це один з найглубших аргументів, коли-небудь застосованих до ігор. Якщо ви хочете, щоб швидкість набрала швидкість, тоді повністю відмовтеся від мов програмування та працюйте з збіркою, що, можливо, є найкращою перевагою на сьогоднішній день.


2
Ця стінка тексту, здається, не додає нічого, про що вже не було сказано в інших відповідях. Будь ласка, відредагуйте свою відповідь, щоб вона була більш зрозумілою, і будь ласка, переконайтесь, що ваші відповіді стосуються балів, не піднятих іншою відповіддю. В іншому випадку, будь ласка, подумайте про видалення відповіді, оскільки це лише додає шуму.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.