Хороший Открытый исходный код для изучения [закрытого] Веб-программирования

Резюме : попытайтесь выделить память рядом со статическим кодом. Но для вызовов, которые не могут дозвониться с помощью rel32, вернитесь к call qword [rel pointer] или встроенным mov r64,imm64 / call r64.

Ваш механизм 5., вероятно, лучше всего работает, если вы не можете 2. работать, но 4. это легко и должно быть хорошо. Direct call rel32 также нуждается в некотором предсказании ветвлений, но он определенно все же лучше.


Терминология: «встроенные функции», вероятно, должны быть «вспомогательными» функциями. «Внутренний» обычно означает встроенную в язык (например, значение на Фортране) или «не настоящую функцию , просто что-то, что указывает на машинную инструкцию» (C / C ++ / Rust означает как для SIMD, или как _mm_popcnt_u32(), _pdep_u32() или _mm_mfence()). Ваши функции Rust собираются для компиляции с реальными функциями, которые существуют в машинном коде, который вы собираетесь вызывать с помощью call инструкций.


Да, распределение буферов JIT в пределах + -2GiB от ваших целевых функций, очевидно, идеально, позволяя выполнять прямые вызовы rel32.

Наиболее простым было бы использование большого статического массива в BSS (который компоновщик поместит в пределах 2 ГБ вашего кода) и выделение ваших выделений из этого . (Используйте mprotect (POSIX) или VirtualProtect (Windows), чтобы сделать его исполняемым).

Большинство ОС (включая Linux) выполняют ленивое выделение для BSS (отображение COW на нулевой странице, выделяя только физические фреймы страницы, чтобы поддержать это выделение, когда оно записано, как mmap без MAP_POPULATE), поэтому оно тратит впустую только виртуальное адресное пространство для массива 512 МБ в BSS, для которого используются только нижние 10 КБ.

Не делайте его больше или близким к 2 ГБ, тем не менее, потому что это оттолкнет другие вещи в BSS слишком далеко. Модель «маленького» кода по умолчанию (как описано в x86-64 System V ABI) помещает все статические адреса в пределах 2 ГБ друг от друга для адресации данных, относящихся к RIP, и вызова rel32 / jmp.

Недостаток: вам придется написать хотя бы простой распределитель памяти самостоятельно, вместо того, чтобы работать с целыми страницами с помощью mmap / munmap. Но это легко, если вам не нужно ничего освобождать. Возможно, просто сгенерируйте код, начинающийся с адреса, и обновите указатель, как только вы дойдете до конца, и выясните, как долго ваш блок кода. (Но это не многопоточность ...) В целях безопасности не забудьте проверить, когда вы доберетесь до конца этого буфера и прервитесь или вернетесь к mmap.


Если ваши абсолютные целевые адреса находятся на низком 2 ГБ виртуального адресного пространства, используйте mmap(MAP_32BIT) в Linux . (Например, если ваш код Rust скомпилирован в исполняемый файл не-PIE для Linux x86-64. Но это не относится к исполняемым файлам PIE ( обычно в наши дни ) или к целям в общих библиотеках. Вы можете обнаружить это во время выполнения, проверив адрес одной из ваших вспомогательных функций.)

В целом (если MAP_32BIT не полезно / не доступно), ваша лучшая ставка, вероятно, [ 1115] без MAP_FIXED, но с ненулевым адресом подсказки, который вы считаете свободным.

В Linux 4.17 появилась MAP_FIXED_NOREPLACE , которая позволила бы вам легко искать ближайший неиспользуемый регион (например, шаг на 64 МБ и повторить попытку, если вы получите EEXIST, затем запомните этот адрес, чтобы избежать поиска в следующий раз) , В противном случае вы можете проанализировать /proc/self/maps один раз при запуске, чтобы найти не отображенное пространство рядом с отображением, содержащее адрес одной из ваших вспомогательных функций. Будет близко друг к другу.

Обратите внимание, что более старые ядра, которые не распознают флаг MAP_FIXED_NOREPLACE, обычно (при обнаружении коллизии с существующим отображением) возвращаются к типу поведения «не MAP_FIXED»: они возвращают адрес, который отличается от запрошенного адреса.

blockquote>

На следующих более высоких или более низких свободных страницах было бы идеально иметь непарную карту памяти, чтобы таблице страниц не требовалось слишком много различных каталогов страниц верхнего уровня. (Таблицы страниц HW представляют собой основополагающее дерево.) И как только вы найдете место, которое работает, сделайте будущие распределения непрерывными с этим. Если вы в конечном итоге используете там много места, ядро ​​может оппортунистически использовать огромную страницу размером в 2 МБ, и если ваши страницы снова будут смежными, это означает, что они совместно используют один и тот же каталог родительских страниц в таблицах страниц HW, поэтому iTLB может пропустить запуск страниц . немного дешевле (если эти более высокие уровни остаются горячими в кэшах данных или даже кешируются внутри самого оборудования Pagewalk). И для эффективного для ядра, чтобы отслеживать как одно большее отображение. Конечно, использование большего количества уже выделенной страницы еще лучше, если есть место. Лучшая плотность кода на уровне страницы помогает инструкции TLB, и, возможно, также на странице DRAM (но это не обязательно тот же размер, что и на странице виртуальной памяти).


Затем, когда вы выполняете генерацию кода для каждого вызова, просто проверяет , находится ли цель в диапазоне для call rel32 с off == (off as i32) as i64
[ 1187] остальное отступает до 10-байтовых mov r64,imm64 / call r64. (rustcc скомпилирует это в movsxd / cmp, поэтому проверка каждый раз имеет только тривиальную стоимость для времени компиляции JIT.)

(или 5-байтовый mov r32,imm32, если это возможно. Операционные системы, которые этого не делают) support MAP_32BIT может по-прежнему иметь целевые адреса там внизу. Проверьте это с помощью target == (target as u32) as u64. Третье mov -обеспечивающее кодирование, 7-байтовое mov r/m64, sign_extended_imm32, вероятно, неинтересно, если вы не используете JIT-код ядра для ядра. отображается в высоком 2GiB виртуального адресного пространства.)

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


Альтернативы mov -imm / call reg

mov r64,imm64 - это 10-байтовая инструкция, которая немного велика для выборки / декодирования и для хранения uop-кэша. И может потребоваться дополнительный цикл для чтения из кэша UOP в семействе SnB, согласно микроарху pdf Агнера Фога ( https://agner.org/optimize ). Но современные процессоры имеют довольно хорошую пропускную способность для выборки кода и надежные внешние интерфейсы.

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

Кстати, если какая-либо из ваших функций является переменной, x86-64 System V требует, чтобы вы передали AL = количество аргументов XMM, вы можете использовать r11 для указателя функции. Он закрыт и не используется для передачи аргументов. Но RAX (или другой «устаревший» регистр) сохранит префикс REX в call.


  1. Распределить функции Rust рядом с тем местом, где mmap будет выделять
blockquote>

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

mmap имеет более 4 ГБ свободного виртуального адресного пространства для выбора. Вы не знаете заранее, где он будет выделяться. (Хотя я думаю, что Linux, по крайней мере, сохраняет некоторую локальность для оптимизации таблиц страниц HW.)

Теоретически вы могли бы скопировать машинный код ваших функций Rust, но они, вероятно, другой статический код / ​​данные с режимами RIP-относительной адресации.


  1. call rel32 для заглушек, использующих mov / jmp reg

Похоже, это отрицательно скажется на производительности (возможно, мешает с прогнозом RAS / адреса перехода).

blockquote>

Недостатком перфекта является только наличие 2 общих инструкций по вызову / прыжку, которые передний интерфейс должен пройти, прежде чем он сможет накормить внутренний конец полезными инструкциями. Это не здорово; 5. намного лучше.

Это в основном то, как PLT работает для вызовов функций совместно используемой библиотеки в Unix / Linux, и будет выполнять то же самое . Вызов через PLT (таблица связывания процедур) Функция заглушки почти так же, как это. Таким образом, влияние на производительность было хорошо изучено и сопоставлено с другими способами работы. Мы знаем, что динамические вызовы библиотек не являются причиной падения производительности.

Звездочка перед адресом и инструкциями push, куда она направляется? показывает разбор AT & T одного или одношаговую C-программу, такую ​​как main(){puts("hello"); puts("world");}, если вам интересно. (При первом вызове он выдвигает аргумент arg и переходит к функции отложенного динамического компоновщика; при последующих вызовах целью косвенного перехода является адрес функции в общей библиотеке.)

Почему PLT существует в дополнение к GOT, вместо того, чтобы просто использовать GOT? объясняет больше. jmp, адрес которого обновляется посредством ленивых ссылок, является jmp qword [xxx@GOTPLT]. (И да, PLT действительно использует косвенную память jmp здесь, даже на i386, где перезаписанный jmp rel32 будет работать. IDK, если GNU / Linux когда-либо исторически использовался для перезаписи смещения в jmp rel32. )

jmp - это просто стандартный хвостовой вызов, и не нарушает дисбаланс стека предикторов обратного адреса . Возможное значение ret в целевой функции будет возвращаться к инструкции после исходного call, то есть по адресу, который call помещен в стек вызовов и на микроархитектурный RAS. Только если вы использовали push / ret (например, «retpoline» для смягчения Призрака), вы бы разбалансировали RAS.

Но код в Jump for JIT (x86_64) , который вы связали, к сожалению, ужасен (см. Мой комментарий под ним). Это будет сломать RAS для будущих возвратов. Вы могли бы подумать, что он сломает его только для этого вызова (чтобы получить адрес возврата, который нужно скорректировать), если балансировать push / ret, но на самом деле call +0 это особый случай, который не идет на RAS в большинстве процессоров: http://blog.stuffedcow.net/2018/04/ras-microbenchmarks . (Обращение к nop может измениться, я думаю, но все это совершенно безумие против call rax, если только он не пытается защитить от эксплойтов Spectre.) Обычно на x86-64 вы используете REA-относительный LEA, чтобы получить соседний адрес в регистре, а не call/pop.


  1. inline mov r64, imm64 / call reg
blockquote>

Это, вероятно, лучше, чем 3; Стоимость внешнего интерфейса с большим размером кода, вероятно, ниже, чем стоимость вызова через заглушку, которая использует jmp.

Но это также, вероятно, достаточно хорошо , особенно если ваши методы alloc-inside-2GiB работают достаточно хорошо большую часть времени на большинстве целей, которые вас интересуют.

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


  1. call qword [rel nearby_func_ptr]
blockquote>

Вот как gcc -fno-plt компилирует вызовы функций совместно используемых библиотек в Linux (call [rip + symbol@GOTPCREL]), и как обычно выполняются вызовы функций Windows DLL. (Это похоже на одно из предложений в http://www.macieira.org/blog/2012/01/sorry-state-of-dynamic-libraries-on-linux/ )

[ 11141] call [RIP-relative] имеет размер 6 байтов, всего на 1 байт больше, чем call rel32, поэтому он оказывает незначительное влияние на размер кода по сравнению с вызовом заглушки. Интересный факт: иногда вы видите addr32 call rel32 в машинном коде (префикс размера адреса не имеет никакого эффекта, кроме заполнения). Это происходит из-за того, что компоновщик ослабляет call [RIP + symbol@GOTPCREL] до call rel32, если во время компоновки был обнаружен символ с невидимой видимостью ELF в другом .o, а не в другом общем объекте.

Для вызовов из общей библиотеки это обычно лучше, чем заглушки PLT, с единственным недостатком - более медленный запуск программы, поскольку она требует раннего связывания (не ленивое динамическое связывание). Это не проблема для вас; целевой адрес известен раньше времени генерации кода.

Автор патча проверил его производительность против традиционный PLT на неизвестном оборудовании x86-64. Clang, возможно, является наихудшим сценарием для вызовов совместно используемой библиотеки, поскольку он выполняет много вызовов небольших функций LLVM, которые не занимают много времени, и он долго выполняется, поэтому издержки при раннем связывании при запуске незначительны. После использования gcc и gcc -fno-plt для компиляции clang время для clang -O2 -g для компиляции tramp3d увеличивается с 41,6 с (PLT) до 36,8 с (-fno-plt). clang --help становится немного медленнее.

(в заглушках x86-64 PLT используются jmp qword [symbol@GOTPLT], а не mov r64,imm64 / jmp. Косвенная память jmp - это всего лишь один шаг на современных процессорах Intel, поэтому при правильном прогнозировании это дешевле, но может быть медленнее при неправильном предсказании, особенно если запись GOTPLT отсутствует в кеше. Однако, если она используется часто, она обычно предсказывает правильно, но в любом случае 10-байтовый movabs и 2-байтовый jmp могут быть извлечены как block (если он умещается в 16-байтовом выровненном блоке выборки) и декодировать за один цикл, поэтому 3. не является абсолютно необоснованным. Но это лучше.)

При выделении места для вашего указатели, помните, что они извлекаются как данные в кэш L1d , и с записью dTLB, а не iTLB. Не чередуйте их с кодом, который будет тратить пространство в I-кэше на эти данные, и тратить пространство в D-кэше на строки, содержащие один указатель и в основном код. Сгруппируйте ваши указатели вместе в отдельном 64-байтовом фрагменте из кода, чтобы строка не обязательно находилась как в L1I, так и в L1D. Хорошо, если они находятся на той же странице , что и некоторый код; они доступны только для чтения, поэтому не будут вызывать нюки конвейера с самоизменяющимся кодом.

5
задан Prabu 20 July 2012 в 13:53
поделиться

9 ответов

If I understand you correctly, you want to study the code of popular open source web applications to understand how 'real world, live, popular' applications are coded.

If that is so, I recommend the following popular, open source applications:

  • Wordpress - coded with PHP, this application is a very popular blog management system

  • Text Pattern - coded with PHP, this application is similar to wordpress but slightly more advanced.

  • CGI Module - coded with Perl, this perl module is used in most perl based web applications. Understanding the code of this module will give you great insights on a part of how the internet works.

  • SQLAlchemy - coded with Python, is a popular application in the python community to interface / interact with databases.

It would be easier if you familiarize yourself with one of these languages well before you try to understand the working of any of the above.

3
ответ дан 14 December 2019 в 01:16
поделиться

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

  1. Небольшие проекты. Под маленьким я подразумеваю «почти не проект» маленький. По той или иной причине они так и не смогли пройти стадию доказательства концепции. Либо идеи были недостаточно интересными, либо, что еще хуже, программисты не были достаточно хороши, чтобы перевести их в готовое для прайм-тайм приложение. Зачастую возможности этих программистов не намного превышают ваш текущий уровень, поэтому они не стоят вашего времени.
  2. Средне-маленькие проекты . Эти проекты достаточно велики, чтобы быть интересными и сложными, но общедоступной технической документации крайне не хватает, и вы никогда не станете главой или хвостом их архитектуры. Вы могли бы написать авторам по электронной почте, но они, вероятно, слишком заняты, деля свое время между высокооплачиваемой дневной работой и посвящая все свое личное время этому любимому проекту. Двигаемся дальше ...
  3. Большие проекты времени. Например, Firefox, Eclipse и т. Д. Программное обеспечение в этой категории тщательно документировано, но это не имеет значения, потому что количество времени, необходимое для чтения и понимания всего этого, является непомерно высоким. Вы должны быть безумны, чтобы пройти через это, если вы не работаете над проектом (и, надеюсь, вам платят за это!).

Я не хочу отговаривать вас, это всего лишь мой личный опыт. Если вы хотите стать лучшим программистом, сфокусируйтесь на поиске работы. Темпы обучения в течение моего первого года на работе были на порядок выше, чем любая работа, которую я выполнял самостоятельно.

3
ответ дан 14 December 2019 в 01:16
поделиться

I would advice first to choose a language or a platform. For example groovy on grails. Then take tutorials. Every platform have a "petshop example" or something like that where you can start and follow.

If language doens't matter try Mhonarc which is written in perl.

Luis

1
ответ дан 14 December 2019 в 01:16
поделиться

Ну, этот вопрос немного расплывчатый. Вы ищете веб-фреймворки с открытым исходным кодом или открытый исходный код, написанный в веб-фреймворках? Но попробуйте посмотреть на Tomcat , Django , ASP.net MVC , [1150256 MediaWiki , Drupal и Wordpress среди многих, многих других.

Еще пара приложений (теперь, когда это выяснено) - это Gitweb и Meldware

3
ответ дан 14 December 2019 в 01:16
поделиться

Самая продвинутая фреймворк PHP, который я видел, с красивым кодом: FLOW3 Yet my answer is only relevant, if you are looking for serious framework as the code example, and do know the basics already...

0
ответ дан 14 December 2019 в 01:16
поделиться

If you want to learn from abc then w3schools.com is a better option.

Where you can find lots of seciton like asp,asp.net, HTML etc. Also you will have lots of examples there.

-2
ответ дан 14 December 2019 в 01:16
поделиться

You can check http://www.asp.net and http://www.asp.net/community/projects/ for big open source web projects
http://www.codeproject.com/ for smaller examples
And http://code.google.com/opensource/ for a very big list
And Also http://www.codeplex.com/ , http://sourceforge.net

0
ответ дан 14 December 2019 в 01:16
поделиться

Я бы посоветовал вам взглянуть на Pier Это система управления контентом, написанная на Seaside. Он гораздо лучше структурирован, чем любые другие предложения, которые я видел здесь.

0
ответ дан 14 December 2019 в 01:16
поделиться

Если вы заинтересованы в Python, вы можете посмотреть Reddit или исходный код Django . Я знаю, что Django - это веб-фреймворк, но код действительно хорошо сделан.

1
ответ дан 14 December 2019 в 01:16
поделиться
Другие вопросы по тегам:

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