На этой
странице собраны ссылки на полезные материалы, продолжительное и периодическое изучение которых улучшит ваш
навык разработки программного обеспечения. Она является справочником, которым я сам буду активно пользоваться,
поэтому имейте ввиду, что ссылки могут не только появляться, но и исчезать, если они утратили для меня свою
полезность. Материалы здесь будут для опытных специалистов, которые хотят знать ещё больше. Если у вас на
примете есть полезные материалы, прислать их мне можно через Контакты.
Для лучшего
понимания и усвоения материала из конкретной статьи или видео, я рекомендую делать следующее: вначале полностью
отсмотрите материал, затем выберите оттуда один наиболее понятный тезис и начните активно применять его в
своей работе. Например, тезис о важности грамотного использования кэша процессора для улучшения производительности.
Возьмите любую функцию в своей программе и задайте себе вопрос: «использую ли я затрагиваемые
кэш-линии полностью?». Если ваш ответ «нет» и
веской причины не использовать кэш-линии полностью у вас тоже нет, значит вы нашли проблему в архитектуре своей
программы. На вопрос «и что теперь делать?» отвечать не мне, всё
зависит от вашей задачи, проекта и руководства. Однако в будущем вы уже будете иметь ввиду, что нужно будет
задуматься ещё и об этой проблеме.
Перед переходом к
следующему тезису из рассматриваемого материала, следует довести мышление о текущем тезисе до автоматизма. Моему
мозгу, например, не хватает «мыслительной ёмкости» активно
думать сразу про избегания выделений памяти и вызовов функций по указателям, про грамотное использование кэша
процессора и SIMD-операций, а также про цепочки
зависимостей при вычислениях, параллелизм и т.п., поэтому перед тем как я активно начну думать, например, про
использование SIMD, я достаточно потренируюсь пассивно замечать всё, что в списке идёт
до. Это не так сложно, если не стесняться, что вы чего-то не знаете, и ставить себе задачу тренировать именно
пробелы в знаниях, а не вещи в которых вы чувствуете себя комфортно.
Повторяйте эту
процедуру из полного просмотра материала и проработки тезисов, пока у вас не появятся мысли по улучшению
предложенных техник. Например, если вы сможете сказать что-то наподобие «как он говорит это,
конечно, хорошо, но ещё лучше, если сделать, как я скажу», то вы, скорее всего, достигли
хорошего уровня понимания материала и можете теперь на его основе творить что-нибудь своё. Если вы думаете, что
автор во всём прав и вам добавить нечего, то значит вам ещё предстоит полностью понять этого самого автора, а также
ещё предстоит прочувствовать его советы на собственной шкуре.
Желаю успехов в
учении!
-
Designing and Evaluating Reusable
Components - 2004 от Casey Muratori. Отличный доклад для всех, кто хочет
улучшить навыки проектирования интерфейсов своих компонентов, библиотек и т.п., а также
навыки оценки готовых решений. Как человек, который активно пишет свою
библиотеку, могу подтвердить, что
следование советам из этого доклада действительно поможет сделать функции более универсальными и
переиспользуемыми. Заодно вы начинаете видеть, насколько плохие библиотеки делают другие разработчики (да-да, я
в том числе про тебя, стандартная библиотека Go).
-
CppCon 2014: Mike Acton "Data-Oriented
Design and C++" от Mike Acton. Очень глубокое по своему содержанию введение в
программирование с фокусом на данные. Даже не пытайтесь понять его сходу. Используйте алгоритм из моей
инструкции для постепенного изучения каждого из тезисов. Отмечу,
что идеи из этого доклада существуют минимум с 1970 года, а я думаю, что может быть и с ещё более ранних времён.
Просто в какой-то момент люди начали фокусироваться не на том, в следствие чего даже в образовательных
учреждениях уже «здравому смыслу» о работе с данными больше не
обучают. Зато полно всякого ООП, чистого кода, шаблонов, UML и прочего рефакторинга...
-
Code Clinic 2015: How to Write Code
the Compiler Can Actually Optimize от Mike Acton.
Продолжение разговора на тему программирования через данные. Этот доклад примечателен, помимо стандартного
объяснения важности фокуса на данных, наличием подробного примера решения задачи в качестве тренировки своих
навыков для повышения квалификации и улучшения «чуйки» на скрытые
взаимоотношения данных. В том числе доклад освещает некоторые оптимизации, которые делает компилятор. Мне очень
понравился совет писать присваивания к переменным в static single-assignment форме (т.е.
к переменной присваивать значения только единожды, а также все выражения, где происходят чтения/записи из/в
памяти выносить в отдельные переменные). Это действительно помогает увидеть, где у вас осуществляется доступ к
памяти, и даже без перехода на язык ассемблера прикинуть, насколько хорошо вы используете
кэш-линии.
-
Solving the Right Problems for Engine Programmers - Mike
Acton (TGC 2017) от Mike Acton.
Продолжение разговора на тему программирования через данные. В этом докладе даётся больше конкретных советов и
практик, с которых стоит начинать решение своей проблемы. Также в начале есть ещё небольшой обзор бытующих
советов от, так называемых, «профессионалов» (думаю, вы сами
понимаете, какие на Quora могут быть советы).
-
Lec 16 | MIT 6.189 Multicore
Programming Primer, IAP 2007 от Mike Acton. Здесь
автор пошёл со своей методологией ва-банк. Доклад посвящён переходу с Sony PlayStation 2
на PlayStation 3, поэтому его содержимое несколько специфично, но, как и везде, можно
найти, чему стоит поучиться. Автор рассказывает, что для переноса своего движка на новую
игровую систему пришлось, по сути, всё удалить и написать заново, поэтому понятность и поддерживаемость исходных
текстов его вообще не волнуют. В целом, это правильно, особенно, если вы знаете, что вы проект сделали, и дальше
нужно делать уже что-то новое, а не заниматься длительной поддержкой. Но даже и для проектов, которые нужно
поддерживать, я полностью согласен с моментом, что если программа написана оптимально (т.е. делает только
минимально-необходимое количество операций для выполнения задачи), то её структура и так будет простой и
понятной.
-
Everyone Watching This Is Fired: Tips
for Game Industry Programmers от Mike Acton. Ну
здесь уже о «наболевшем». Если коротко, то разработчики должны
понимать, что они делают, а также уметь донести это до других (команды, начальства, клиента и т.д.). В видео
автор освещает пятьдесят пунктов, которые должны соблюдать абсолютно все разработчики.
Для удобства я
позаимствовал их текстовое изложение и добавил ниже. Рекомендую к ним подступаться также по одной штуке за
раз.
Пятьдесят обязательных для выполнения пунктов.
- I can articulate precisely what problem I am trying to
solve.
It’s all too easy to get stuck in the
weeds and lose track of why you’re doing what you’re doing. Keep top of mind what the actual end goal is do,
and you might spot an alternative path.
- I have articulated precisely what problem I am trying to
solve.
Communicate the problem “out loud” to
other team members, your product manager, etc.
- I have confirmed that someone else can articulate what
problem I am trying to solve.
Communication!
Ensure your team is all on the same page. Make sure your understanding of the problem is complete.
- I can articulate why my problem is important to solve.
If you solve the problem you’re working on, who benefits, and how much?
- I can articulate how much my problem is worth solving.
If you say it’s worth “as long as it takes”… Mike does not have friendly
words for you. For any problem there’s a maximum amount of time and effort worth investing in
solving it. At least have some idea of the upper bound.
- I have a Plan B in case my solution to my current problem
doesn’t work.
Imagine you’re days or hours
before the deadline, and you can tell that completing Plan A will be impossible. What do you do instead?
Maybe you have a simplified algorithm, or you can disable a certain subsystem. Have more than one plan.
- I have already implemented my Plan B in case my solution to
my current problem doesn’t work.
Mitigate risk
by writing the backup version first. This means you always have a safety net and you can learn more about
the problem space in order to iterate on Plan A.
- I can articulate the steps required to solve my current
problem.
Programming only works when you break
down problems into manageable chunks. Have a sketch of the steps to the end state before you begin.
- I can clearly articulate unknowns and risks associated with
my current problem.
There are always going to
be things you don’t know. You should know where they are in your plan, so you can manage them.
- I have not thought or said “I can just make up the time”
without immediately talking to someone.
Say
it’s Wednesday, you have a project due on Friday, and you get some new task dropped on your lap. You think
“I’ll do the new thing now, and make up the time for the original task by Friday”… mistake! Communicate
about the conflict on Wednesday. Your product manager will help manage the timing and risk.
- I write a “framework” and have used it multiple times to
actually solve a problem it was intended to solve.
If you’re writing a tool of some kind, you should verify it works in practice. Too often
people create something in isolation and it doesn’t end up delivering in the real world.
- I can articulate what the test for completion of my current
problem is.
If you don’t know when to stop, you
might find yourself going down rabbit holes, chasing unimportant marginal gains.
- I can articulate the hypothesis related to my problem and
how I could falsify it.
If a hypothesis cannot
be proven wrong, there’s no knowledge to be gained. As Karl Popper showed, science only works through
falsification.
- I can articulate the (various) latency requirements for my
current problem.
Any time you write code, you
should consider when the output data is required. Not every caller needs output data instantly, and nor do
you have an unbounded amount of time to perform everything. At least get an idea of the sensible bounds.
- I can articulate the (various) throughput requirements for
my current problem.
How much data needs to come
through the system? How many bytes, requests, or frames per second?
- I can articulate the most common concrete use case of the
system I am developing.
You should know what
actual users of your system will actually be doing most of the time. Having a vague idea doesn’t help, since
knowing which patterns are common informs which way to write a given piece of code.
- I know the most common actual, real-life values of the data
I am transforming.
Beyond use cases, you should
know the data inside the system. For example, if your function works with integers, you’d probably write it
quite differently if 99% of the values are 0.
- I know the acceptable ranges of values of all the data I am
transforming.
Computer systems always have
limits. Know the ranges for the data types you’re working with (and enforce them).
- I can articulate what will happen when (somehow) data
outside that range enters the system.
Murphy’s
Law says “anything that can go wrong will go wrong”. Know how your system will behave in such cases, and
handle such problems if necessary.
- I can articulate a list of input data into my system roughly
sorted by likelihood.
Have an idea of the space
of possible data, what’s most likely, second most likely, etc. Code appropriately, for example checking for
common error conditions first.
- I know the frequency of change of the actual, real-life
values of the data I am transforming.
Reason
about the frequency of change and figure out how often you’ll want to calculate derived values.
- I have (at least partially) read the (available)
documentation for the hardware, platform, and tools I use most commonly.
Read the friendly manual! Go a step beyond day-to-day reference, and try
reading the full documentation to gain a deep understanding.
- I have sat and watched an actual user of my system.
Watching users can massively break shift your view of how your software
works. Do it!
- I know the slowest part of the users of my system’s workflow
with high confidence.
Any workflow has a
bottleneck. Make sure you know what it is so you can focus efforts there, if need be.
- I know what information users of my system will need to make
effective use of the solution.
Think about what
documentation or data users need to understand and use your solution.
- I can articulate the finite set of hardware I am designing
my solution to work for.
Software requires
hardware. Know what hardware your program targets, such as:
- CPU architectures
- Minimum requirements for memory, CPU speed, and network
bandwidth
- Input devices
- Output devices
- The environment the hardware runs in (e.g. data centre or living
room)
- I can articulate how that set of hardware specifically
affects the design of my system.
If you’re
targeting low end devices, how do you ensure you don’t exhaust memory? If some users don’t have pointing
devices, how do you accommodate them?
- I have recently profiled the performance of my system.
If you’re developing a local app, run profiling tools regularly to gain
an idea of performance over time. With server based programs, you can install an APM (Application
Performance Monitoring) tool in production and have continuous profiling data.
- I have recently profiled memory usage of my system.
Make sure you aren’t wasting memory.
- I have used multiple different profiling methods to measure
the performance of my system.
There’s no
perfect profiling tool, so know how to use more than one.
For example, some great Python profilers are
cProfile,
py-spy,
Austin,
Scalene,
Fil, and
memray. They all have different
characteristics and complement each other.
- I know how to significantly improve the performance of my
system without changing the input/output interface of the system.
Do you know the next step to optimize your system? You don’t have to do it right now, as
it may not be worth it, but you should have an idea what you’d do next to make your code faster. For
example, use a faster but less convenient data structure, or convert a hot function into a faster language
(such as Python to C with Cython).
This should also guide you to designing interfaces that are
optimizable in the first place. For example, don’t commit to returning expensive-to-compute results
immediately, but instead return a promise.
- I know specifically how I can and will debug live release
builds of my work when they fail.
Bugs are
inevitable. You should know the tools that will let you work through those problems in production, such as
logs, debuggers, or a live shell.
- I know what data I am reading as part of my solution and
where it comes from.
Know where the data comes
from, in what format, and how you can read it.
- I know how often I am reading data I do not need as part of
my solution.
Data access is rarely optimal.
You’ll often be moving data that’s not required for your solution, such as unnecessary fields or wrapper
objects. If you don’t know about this waste, you can’t reason about whether it’s worth the overhead or not.
- I know what data I am writing as part of my solution and
where it is used.
All output data is intended
for use by a human or another program. Be organized enough to know what the downstream consumers of your
output are.
- I know how often I am writing data I do not need to as part
of my solution.
Data output is also rarely
optimal. Are you frequently writing out data that hasn’t changed? Are you writing many fields when only one
is used downstream? Again, know about it so you can reason about it.
- I can articulate how all the data I use is laid out in
memory.
Many programming languages and
frameworks can handle memory for you, but that doesn’t abdicate you of responsibility. Know how your tools
lay out memory, so you can tell when another approach makes sense.
For example, in Python most objects are based on dictionaries, so you
should have a solid understanding of how they work, and alternatives like
slotted
classes or arrays.
- I never use the phrase “platform independent” when referring
to my work.
Any system depends on many things
below it. Know what they are.
- I never use the phrase “future proof” when referring to my
work.
Future-proofing is “100% a fool’s
errand”. “You can’t pre-solve problems you have no information of.”
- I can schedule my own time well.
“You’re an adult person, just use a calendar.”
- I am vigilant about not wasting others’ time.
Don’t waste time asking questions that you can google in five seconds.
But also don’t waste loads of time struggling for days alone when you could get help from a team member in
minutes! Find the balance.
- I actively seek constructive feedback and take it seriously.
Ask for feedback and do something about it.
- I am not actively avoiding any uncomfortable (professional)
conversations.
If there’s something wrong at
work, don’t put off talking about it.
- I am not actively avoiding any (professional) conflicts.
If you’ve noticed something is going wrong, whether technically or
communication wise, get those conflicts out in the open. Letting them stew never helps.
- I consistently interact with other professionals,
professionally.
Be courteous and professional!
Mike jokes about setting an incredibly low bar: no yelling, no hitting, …
- I can articulate what I believe others should expect from
me.
Have a standard for yourself and be ready
to tell your co-workers what it is.
- I do not require multiple reminders to respond to a request
or complete work.
“Waiting for someone else to
poke you is not an effective way to get your job done.”
- I pursue opportunities to return value to the commons (when
appropriate).
All our work builds on top of the
work of countless others. At some point, you’ll have opportunities to give back to the community at large.
For example, talking at meetups, making open source contributions, or even just discussing topics with your
team to boost everyone’s skills.
- I actively work to bring value to the people I work with.
You’re part of a team, so work to help them.
- I actively work to ensure under-represented voices are
heard.
Don’t stand by leaving this to be
someone else’s problem. Do something to make sure that minorities are heard. This might mean ensuring that
the minority person at work gets a chance to speak, that your hiring process is unbiased, or that your
website is accessible for users who rely on screen readers.
Пока пусто ㋡.
-
Optimization manuals от
Agner Fog. Я пока прочитал только чуть больше половины первой книги и хочу сказать, что это неплохое
введение в программирование на C/C++ под относительно современные процессоры
Intel, AMD или VIA. Сложно сказать насколько я рекомендую
это к прочтению, но думаю лишним точно не будет.
|