Повышение производительности при переписывании кода C # на C / C ++

В дополнение к тем комментариям, которые упоминают реализацию IPet/PetBase, существуют также случаи, где обеспечение класса помощника средства доступа может быть очень ценным.

стиль IPet/PetBase предполагает, что у Вас есть несколько реализаций, таким образом увеличивающих стоимость PetBase, так как это упрощает реализацию. Однако, если у Вас есть реверс или смешение двух, где у Вас есть несколько клиентов, обеспечивание справки класса помочь в использовании интерфейса может уменьшить стоивший, облегчив использовать интерфейс.

17
задан Cœur 3 September 2017 в 13:53
поделиться

9 ответов

Во-первых, профилируйте свой код. Возможно, вы найдете настоящего настоящего создателя, который значительно ускоряет код на C #.

Во-вторых, написание кода на C с использованием указателей не совсем справедливое сравнение. Если вы собираетесь использовать указатели, почему бы не написать их на языке ассемблера и добиться реальной производительности? (Не совсем, просто reductio ad absurdam .) Лучшее сравнение для нативного кода было бы использовать std::string. Таким образом, вы все еще получаете большую помощь от класса string и безопасности исключений C ++.

Учитывая, что вам нужно прочитать 2-3 миллиона записей из БД, чтобы выполнить эту работу, я очень сомневаюсь, что время, потраченное на взлом строк, перевесит истекшее время, затраченное на загрузку данные из БД. Итак, рассмотрим вместо этого, как структурировать ваш код так, чтобы вы могли начать обработку строк, пока идет загрузка БД.

Если вы используете, скажем, SqlDataReader для последовательной загрузки строк, должна быть возможность как можно быстрее объединить N строк и передать их в отдельный поток для последующей обработки, которая является вашей. текущая головная боль и причина этого вопроса. Если вы используете .Net 4.0, это проще всего сделать с помощью Task Parallel Library и System.Collections.Concurrent также могут быть полезны для сопоставления результатов между потоками.

Этот подход должен означать, что ни задержка БД, ни обработка строк не являются узким местом, так как они происходят параллельно. Это применимо , даже если вы работаете на однопроцессорном компьютере , потому что ваше приложение может обрабатывать строки, пока оно ожидает, когда следующая партия данных вернется из БД по сети. Если вы обнаружите, что обработка строк самая медленная, используйте для этого больше потоков (т. Е. Task). Если БД является узким местом, то вам нужно искать внешние средства для повышения ее производительности - оборудование БД или схему, сетевую инфраструктуру. Если перед обработкой большего количества данных вам нужны результаты, TPL позволяет создавать зависимости между Task и координирующим потоком.

Суть в том, что я сомневаюсь, что стоит перепроектировать все приложение на нативном C или чем-то еще. Есть много способов снять кожу с этой кошки.

10
ответ дан 30 November 2019 в 12:19
поделиться

Один из вариантов - переписать код C как небезопасный C #, который должен иметь примерно одинаковую производительность и не влечет за собой никаких штрафов за взаимодействие.

10
ответ дан 30 November 2019 в 12:19
поделиться

Здесь уже есть несколько довольно хороших ответов, особенно @Steve Townsend.

Тем не менее, мне показалось, что стоит подчеркнуть ключевой момент: По сути, нет никаких причин, по которым код C «будет быстрее», чем код C # . Эта идея миф. Под капотом они оба выдают машинный код, который работает на одном и том же процессоре. До тех пор, пока вы не попросите C # выполнить больше работы , чем C, тогда он может выполнять то же самое.

Переключившись на C, вы заставили себя быть более скромным (вы избежали использования высокоуровневых функций, таких как управляемые строки, проверка границ, сборка мусора, обработка исключений и т. Д., И просто рассматривали ваши строки как блоки необработанных байтов). Если бы вы применили эти низкоуровневые методы к своему коду C # (то есть обработали ваши данные как необработанные блоки байтов, как вы это делали в C), вы бы обнаружили гораздо меньшую разницу в скорости.

Например: на прошлой неделе я переписал (на C #) класс, который написал младший (также на C #). Я достиг 25-кратного улучшения скорости по сравнению с исходным кодом, применив тот же подход, который использовал бы , если бы я писал его на C (то есть , думая о производительности). Я добился того же ускорения, на которое вы претендуете, даже не переходя на другой язык.

Наконец, то, что отдельный случай может быть сделан в 24 раза быстрее, это не означает, что вы можете сделать всю программу в 24 раза быстрее по всем направлениям, перенеся все это на C. Как сказал Стив, профилируйте его, чтобы работать там, где он медленный и тратить свои усилия только там, где это принесет значительные выгоды. Если вы слепо конвертируете в C, вы, вероятно, обнаружите, что потратили много времени на то, чтобы сделать уже работающий код менее понятным.

(PS Моя точка зрения основана на 29-летнем опыте написания кода на ассемблере, C, C ++ и C # и понимания того, что язык - это всего лишь инструмент для генерации машинного кода - в случае C # против C ++ и C это в первую очередь навыки программиста, а не используемый язык, который определяет, будет ли код выполняться быстро или медленно. Программисты на C / C ++, как правило, лучше программистов на C #, потому что они должны быть - C # позволяет вам быть ленивым и быстро писать код в то время как C / C ++ заставляет вас выполнять больше работы, а написание кода занимает больше времени, но хороший программист может добиться высокой производительности на C #, а плохой программист может вырвать ужасную производительность на C / C ++)

4
ответ дан 30 November 2019 в 12:19
поделиться

Нет смысла писать на C поверх C ++, а C / C ++ не существует.

Показатели эффективности маршаллинга довольно просты. Если вам придется маршалировать каждую струну индивидуально, то ваша производительность будет отстойной. Если вы можете собрать все десять миллионов строк за один вызов, то сортировка не будет иметь никакого значения вообще. P / Invoke - не самая быстрая операция в мире, но если вы вызываете ее только несколько раз, это не имеет большого значения.

Может быть проще переписать ваше основное приложение на C ++, а затем использовать C ++ / CLI для объединения его с концом базы данных C #.

4
ответ дан 30 November 2019 в 12:19
поделиться

Поскольку строки являются неизменяемыми в .NET, я не сомневаюсь, что оптимизированная реализация C превзойдет оптимизированную реализацию C # - , без сомнения!

]

В P / Invoke возникают накладные расходы, но если вы реализуете большую часть логики в C и предоставляете только очень гранулированный API для C #, я думаю, вы в гораздо лучшей форме.

В конце концов, написание реализации на C означает больше времени, но это даст вам лучшую производительность, если вы подготовитесь к дополнительным затратам на разработку.

2
ответ дан 30 November 2019 в 12:19
поделиться

Ознакомьтесь со смешанными сборками - это лучше, чем Interop. Interop - это быстрый способ справиться с нативными библиотеками, но смешанные сборки работают лучше.
Смешанные сборки на MSDN
Как обычно, главное - это тестирование и измерение ...

2
ответ дан 30 November 2019 в 12:19
поделиться

Для объединения длинных или множественных строк всегда используйте StringBuilder. Не все знают, что StringBuilder можно использовать не только для ускорения конкатенации строк, но и для вставки, удаления и замены символов.

Если это не достаточно быстро для вас, вы можете использовать char- или byte-массивы вместо строк и работать с ними. Если вы закончили с манипуляциями, вы можете преобразовать массив обратно в строку.

В C # также есть возможность использовать небезопасный код , чтобы получить указатель на строку и изменить неизменяемую строку, но я бы не стал этого рекомендовать.

Как уже говорили другие, вы можете использовать управляемый C ++ (C ++ / CLI) для приятного взаимодействия между .NET и управляемым кодом.

Не могли бы вы показать нам код, может быть, есть другие варианты оптимизации?

0
ответ дан 30 November 2019 в 12:19
поделиться

Когда вы начинаете оптимизировать программу на более позднем этапе (приложение было написано без учета оптимизации), вы должны выявить узкие места.

Профилирование - это первый шаг, чтобы увидеть, куда идут все эти циклы ЦП.

Просто имейте в виду, что профилировщики C # будут только профилировать ваше приложение .Net, а не сервер IIS, реализованный в ядре, и сетевой стек.

И это может быть невидимым узким местом, которое на несколько порядков превосходит то, на что вы ориентируетесь, пытаясь добиться прогресса.

Там вы думаете, что не имеете никакого влияния на IIS, реализованный как драйвер ядра - и вы правы.

Но вы можете обойтись без него - и сэкономить много времени и денег.

Поместите свой талант туда, где это может иметь значение, а не туда, где вы вынуждены бежать со связанными ногами.

0
ответ дан 30 November 2019 в 12:19
поделиться

Присущие различия, как правило, приведены в 2 раза меньше ресурсов процессора, в 5 раз меньше памяти. На практике лишь немногие люди достаточно хороши в C ++ и получают преимущества.

Существует дополнительная выгода для экономии на поддержке Unicode, но только вы можете знать свое приложение достаточно хорошо, чтобы знать, безопасно ли это.

Сначала используйте профилировщик, убедитесь, что вы не привязаны к вводу / выводу.

0
ответ дан 30 November 2019 в 12:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: