ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

すき焼き鍋からタスク並列処理

この記事はユニファAdvent Calender 2020の17日目の記事です。

こんにちは、プロダクトエンジニアリング部のちょうです。最近天気は段々寒くなってきましたね。こんな時期欠かせない料理といえば鍋でしょう。鍋は煮ながら食べる料理ですゆえ、準備の手間はかからないし、好きな食材をどんどんいれることができます。

どころで、最近どれぐらい早く鍋をできるのを考えていました。例えば、炊飯器を15分でご飯を炊けるとする。その15分で鍋の食材を何も処理していない状態からホカホカの鍋にできるのでしょうか。

ここで、この鍋を人気のすき焼き鍋にしましょう。すきやき鍋は必要な食材はこちらです。

レシピから抜粋

  • 牛ロース肉(薄切り)
  • ねぎ
  • しらたき
  • えのきたけ
  • 焼き豆腐
  • しいたけ
  • 白菜
  • サラダ油

割り下については、市販のすき焼きのたれを代用。

ざっくりな料理の流れは

  1. 具材の下処理
  2. サラダ油でねぎと牛肉を炒めて、すきやきのたれを回しかける
  3. 鍋に具材を入れて煮込み

家に二口のコンロはあれば、炒めるのと鍋の煮込みを同時にできます。そして下処理も同時にします。フライパンが熱くなるまではちょっと時間がいります。鍋に水とすきやきのたれを入れて沸騰するまでは5分ぐらいかかります。なので

  • 鍋に水とすきやきのたれを入れて煮る
  • ねぎを下処理する
  • フライパンにサラダ油を入れて中火にする
  • 牛ロース肉を下処理する
  • フライパンが熱くなったらネギをフライパンにいれる
  • ネギに焦げ目がちょっとでたら、牛ロース肉をフライパンいれる
  • のこり時間でほかの具材を下処理する(硬いから柔らかいもの順)
  • 硬いものから柔らかいもの順で鍋にいれる

が一番効率でしょう。

すきやき鍋料理の流れ
すきやき鍋料理の流れ

この料理の流れを技術的な視点からみると、コンロ2口プラス料理する人で実質三プロセッサーです!言い方はちょっと変かもしれませんが(料理する人はコンロではない)、同時に処理することでスピードが上がったのはたしかです。

並列処理の中で、これはタスク並列処理と分類されると思います。もうひとつ、データ並列処理がありますが、それはすべてのプロセッサーはが別々のデータに同時に同じ処理を行うというタイプです。

タスク並列処理では、各タスクの間依頼関係があると、その前後関係が決められます。逆に依頼関係がないと、タスクは同時に行うことができます。例えば、フライパンでネギと牛肉を炒めるのは、先にネギをいれるのです。牛肉はあとなので、 ネギ > 牛肉 という関係があります。そして、フライパンが熱くなるまでネギをいれないので、 フライパンが熱くなる > ネギ という関係があります。最後、ネギを下処理しないと、フライパンにいれないので、 下処理前のネギ > ネギ という関係があります(同じく 下処理前の牛肉 > 牛肉 )。まとめると

  • ネギ > 牛肉
  • フライパンが熱くなる > ネギ
  • 下処理前のネギ > ネギ
  • 下処理前の牛肉 > 牛肉

並列処理では、この関係を満たされば問題ないです。一つの答えは

料理する人 コンロ
ネギを下処理する
牛肉を下処理する サラダ油を入れ、中火
ネギをいれる
牛肉をいれる

タスク並列処理ではどれぐらい早くなるのは、依頼関係の洗い出し及びプロセスの設計が重要です。

ネギの下処理とコンロの中火を一緒やってしまうと、下処理に時間がかかって、コンロに警告が出て自動的に止まることもあります。安全のために、ネギを先に処理するのがおすすめです。こういうタスク間で直接ではない関係性も含めて考慮しましょう。

ではプログラムでシミュレーションしましょう。

import org.junit.jupiter.api.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class SukiyakiTest {

    class StoveCell() : Cell() {
        private var waitingForBeef = false

        override fun receive(context: CellContext, event: Event) {
            when (event) {
                TurnOnStoveEvent -> context.schedule(500, TimeUnit.MILLISECONDS, FryingPanReadyEvent)
                FryingPanReadyEvent -> context.parent.tell(event)
                AddLeekToFryingPanEvent -> {
                    waitingForBeef = true
                    context.schedule(2000, TimeUnit.MILLISECONDS, WaitingForBeefEvent)
                }
                AddBeefToFryingPanEvent -> {
                    if (waitingForBeef) {
                        waitingForBeef = false
                    }
                    context.schedule(4000, TimeUnit.MILLISECONDS, DoneEvent)
                }
                WaitingForBeefEvent -> if (waitingForBeef) {
                    context.logger.info("牛肉を待つ")
                }
                DoneEvent -> context.parent.tell(event)
                PoisonPill -> context.stopSelf()
            }
        }
    }

    class ChefCell(private val latch: CountDownLatch) : Cell() {
        private var _stove: CellRef? = null

        private val stove: CellRef
            get() = _stove!!

        override fun start(context: CellContext) {
            _stove = context.startChild(StoveCell())
            context.logger.info("ネギを下処理する")
            context.schedule(1000, TimeUnit.MILLISECONDS, TurnOnStoveEvent)
        }

        override fun receive(context: CellContext, event: Event) {
            when (event) {
                TurnOnStoveEvent -> {
                    stove.tell(TurnOnStoveEvent)
                    context.logger.info("フライパンにサラダ油をいれ、中火")
                    context.logger.info("牛肉を下処理する")
                    context.schedule(2000, TimeUnit.MILLISECONDS, AddBeefToFryingPanEvent)
                }
                FryingPanReadyEvent -> {
                    context.logger.info("フライパンにネギをいれる")
                    stove.tell(AddLeekToFryingPanEvent)
                }
                AddBeefToFryingPanEvent -> {
                    context.logger.info("フライパンに牛肉をいれる")
                    stove.tell(event)
                }
                DoneEvent -> {
                    context.logger.info("できあがり")
                    stove.tell(PoisonPill) // to stop stove
                    latch.countDown()
                }
            }
        }
    }

    object TurnOnStoveEvent : Event
    object FryingPanReadyEvent : Event
    object AddLeekToFryingPanEvent : Event
    object WaitingForBeefEvent : Event
    object AddBeefToFryingPanEvent : Event
    object DoneEvent : Event

    @Test
    fun test() {
        val latch = CountDownLatch(1)
        val system = CellSystem()
        system.add(ChefCell(latch))
        system.start()
        latch.await()
        system.stop()
    }
}

CellSystemが私が作ったActorモデルのスケジューラーシステムです。Actorモデル自体もタスク並列処理にピッタリするモデルです。コードの中各ステップの時間:

  • ネギ処理 1
  • 牛肉処理 2
  • フライパン熱くなるまで 1
  • ネギを入れてから牛肉を入れるまで 2
  • 牛肉をいれてからできあがり 4

単位は秒ですが、実質は10秒20秒単位と思ってください。

結果

2020-11-30 16:42:32.888 [worker-0] INFO  cell://ChefCell - ネギを下処理する
2020-11-30 16:42:33.897 [worker-0] INFO  cell://ChefCell - フライパンにサラダ油をいれ、中火
2020-11-30 16:42:33.897 [worker-0] INFO  cell://ChefCell - 牛肉を下処理する
2020-11-30 16:42:34.400 [worker-1] INFO  cell://ChefCell - フライパンにネギをいれる
2020-11-30 16:42:35.902 [worker-1] INFO  cell://ChefCell - フライパンに牛肉をいれる
2020-11-30 16:42:39.908 [worker-1] INFO  cell://ChefCell - できあがり

ここでworker-0は料理をする人です。worker-1はコンロです。

いかがでしょうか。普段の生活からも並列処理に関する知識も潜んでいますね。並列処理が人間の生活をモデルにしたといっても過言ではありませんね。時間があったら、ぜひまわりのことをタスク並列処理で考えてみてください。

最後に、ユニファが一緒に働いてくれるメンバーを募集しています。興味がある方ぜひ見てください。

採用情報 - ユニファ株式会社

Local Development with LocalStack

こんにちは。プロダクトエンジニアリング部の杉本です。

この記事は、UniFaアドベントカレンダー2020 の16日目の記事になります。
(私事ですが、2020年6月にユニファにjoinして半年がたち、初めてのブログ投稿です!)

皆さんは、AWSマネージドサービスに依存する機能を開発する場合、ローカルでどのように動作確認をされていますか?

  • 実際のAWS環境にローカルから接続する
  • レスポンスをスタブ化する*1
  • ローカルはそこそこに、とりあえず検証環境にデプロイして確認する

他にも様々あり、プロジェクトのフェーズや事情によっていずれも選択肢になりえると思います。
でもそれに相まって、多少なりともかかるコスト誤接続や誤操作によるリスク動作確認不十分な品質 など課題もでてきますよね。。。

今回は、そんなときにオススメな LocalStackを紹介したいと思います。

続きを読む

7月から始めた英会話のお話

こんにちわ。プロダクトマーケティングに所属している鈴木です!

この記事は、UniFaアドベントカレンダー 15日目の記事となっております。

そろそろ新しい年をお迎えすることになりますので、新しいことを始めたいと考える方も増えそうということで、 少し前に英会話を始めまして、今までのことを書かせていただければと思います。 英会話を始めようと思っている方の役に立てば幸いです。

私はDMM英会話を使っており、その前提でお話をさせていただきます。

また、英会話レッスン25分+15分の予習や復習で1日の英会話に使う時間は40分ほどです。

ちょっと先生が怖そうな描写もありますが、基本的に先生方はとてもやさしい人が多いです。

始める前の状態


質問されても言っている意味がわからないし、なんて返答すればいいかもわからないレベル。

辛うじて読むことはできる。がしかし単語力がほぼ失われている状態。

はじめの1週間 


先生の困惑した表情とため息に何度も心が砕けそうになります。

こんなに話せないものなのね。。。と愕然しました。

このタイミングでやったレッスン内容は写真を英語で描写するというものです。

言いたいことをグーグル翻訳使って翻訳し、それを伝えるので精一杯でした。

始めてから1か月


はじめの1週間でやることが何となくわかってくるので、すこし予習(ズル)をするようになりました。 レッスンで使いたい教材は事前に選べるのでグーグル翻訳を使って写真を描写してメモしてました。

そのメモを読むときは自信満々なのですが、それ以外は急に勢いが無くなるイメージです。 だた、やっと自力で頑張って伝えてみたりするようになります。間違っているので伝わらないのですが。

来る日も来る日も写真の内容を英語で描写する毎日です。

始めてから2か月


2か月目も写真を描写し続けました。 写真題材にレベルがあり、ちょっと難しいのを選んでみようと思えるようになります。 そして、もちろん完ぺきではないのですが写真描写に飽きるという状態がやってくるのです。 写真を描写するのは伝えたい文章を自分で作るトレーニングになるのですが、日常生活であんまり話すイメージが湧かず、もっと会話の方を鍛えたいと思いました。

最初では考えられない状態です。

始めてから3か月


3か月目からは、デイリーニュースという長文の文章を読むレッスンに切り替えました。 これは先生が読んでくれたあとに、自分も後に続いて読むというのを行い、内容確認のための質問や自分の考えを聞くような質問をされる内容です。

これが、中学校の授業のような感じで私としては非常に面白くなかったので1週間ほどでやめました。 次に、選んだのがフリートークになります。英語を話せるようになりたいのであれば話さないとだめだろう。であればフリートークだよね。という考えでした。

ただ、フリートークが思っていた以上にハードルが高く、いかにまだまだなのかと思い知らされる結果になりました。

まずフリートークなので話題が必要になります。「で?何について話したい?」と言われたりすることがあるのですが、正直こちらとしては何でもいいんですよね・・・。うまい先生は話題を広げたり、質問してくれたりするのですが、なかなかうまくいかないこともあります。 この時の対処法としては、後々思いついたのですが最近自分に起こったことを話す。というものです。 そうすると、自分事で作文するので良いトレーニングになります。

次に先生が何言っているかわからないです。テキストがないので。 正直笑顔で、わかったふりもたくさんしました。今でもしてます(笑)

最後になんて言っていいかわからないです。すぐに文章なんて作れないので。 なので、聞いた単語で言いたいことを何となく把握して、グーグル翻訳で翻訳して伝えるということを繰り返してました。

ただ、あまりにもスムーズじゃないので、「レッスンのテキスト使った方がいいよー」と言われることが良くありました。

やっぱりフリートークもやめようか・・・写真を描写に戻ろうか・・・ と何度も脳裏を過りますがここは踏ん張りどころだな。と思い耐えました。 話したいなら話し続けなければと。

始めてから4か月


なかなか難しいな・・・。と思っていたのでどうしたらいいかといろいろ悩んだ末、このころから取り入れたのがシャドーイングというトレーニング方法でした。英語の音声を追いかけて読むようなトレーニングで、同じ文章を何回も読みます。YouTubeなどで無料のがあるのでそれを使ってます。1日10分とか15分程度ですが...。 これの効果も相まって?耐えたから?少し話すのが楽しくなる場面が出てきます。 全然拙いのですが、先生もうんうんと聞いてくれて「つまりこう言いたいんだよね?」 と教えてくれることが多くなった気がします。 コロナで仕事以外では誰とも話さないことも多いので、ちょっとこの時間がたのしみ。とすら感じるようになります。

始めてから5か月


5か月目もひたすらフリートークを続けました。 4か月目よりは少しまともな会話はできてきたかなと感じます。 先生とコロナの状況を話したり、いろいろな国のことを聞いたり、女性の先生から彼氏の愚痴をずっと聞いたりもました。「ケンカ中で、彼の言い方がムカつく」のだそうです。 一番面白かったのはスターウォーズの話から宇宙の話になり、「もしこの宇宙で生物が存在するのは地球だけだとしたら孤独だよね」という話をして「同じ地球人なんだからみんな仲良くしないとですね」という会話もしました。グローバル感が凄いですね。(笑)

ちなみに、先生の多くはネットフリックスユーザーが多いのでネットフリックスの話題かアニメの話(ジブリと新海誠)や漫画(ワンピース・ナルト・鬼滅の刃)の話はとても盛り上がります。 日本のことを聞かれたり、今まで行った海外旅行の話なんかも良く聞かれます。

まだまだ拙い英語力で単語の一点返しで伝えてしまうこともありますが、極力文章を組み立てながら話そうと努力しています。

最後に...


学習って大体同じような工程だなぁと思っています。よくRPGなどに例えられていますよね。 進め方も攻略も自分次第。 Pythonも鋭意勉強中なのですが、何度も壁にぶつかってやっとほんのり使えるかも。となってきた次第です。また高い壁が見えてます。 道のりは長いですがPythonも英会話もその工程も含めて楽しみながらマスターできればと思います。


ユニファでは保育現場の課題解決のため家族コミュニケーションを豊かにするために一緒に働いてくれる仲間を募集中です!!

www.wantedly.com

転職して約半年、振り返ってみるとチームの良さが見えてきた

こんにちは。QAの坂口です。 この記事は、ユニファAdvent Calender 2020の14日目の記事になります。

私は今年8月にユニファへジョインしまして、品質保証業務に携わらせていただいています。 本投稿では、私の業務内容、弊社QAチームの魅力、そしてユニファの社風について書いてみようと思います。

続きを読む

スキルマップ作って採用につなげた話

こんにちわ。ユニファでUIデザイナーをしている中村です。
この記事は、UniFaアドベントカレンダー 13日目の記事です。

まだ入社して3ヶ月程度で、つい先日試用期間が終了いたしました。特に何も変わらないのですが、試用期間をぶじに生き抜くことができてホッとしています。

さて、いきなり本題ですが、先日チームメンバーみんなで「スキルマップ」というものを作りました。

もともとこういうのは作りたいよね、みんなの得意不得意がわかるといいよね、最近新しい人も入ってきたしね(ぼくですね)、などとは話していたのですが、新しいメンバーを採用する際に、いよいよチームとしてどういう人を採っていきたいのか、という課題が浮き上がってきまして。

じゃあ、それを解決するにはみんなのスキルを可視化する必要があるよねということで、そんなこんなでチームスキルの可視化プロジェクトが人知れずぼくの手に渡りました。

スキルマップづくり

f:id:unifa_tech:20201208211709j:plain
マップで使う用にとりあえずスキルいっぱい並べてみた。クリックで拡大します

スキルマップと一口にいっても、様々な方法論、アウトプットがあります(スプレッドシートで◯Xつけるとか)。今回はどうしようかと思ったのですが、ちょうどこの話の前後でMiroの話題が出ており、みんな興味ありそうだったのでMiroでやってみることにしました。Miroの説明は割愛しますが、オンラインホワイトボードです。あ、説明してしまいました。

f:id:unifa_tech:20201208212123j:plain
ぼくのスキルマップです。JSまわりは苦手ですが好きです。プレゼンは機会あったのでわりと得意ですが好きではないです。
大きな画像はこちらです。

どこかで見たスキルマップの記事を参考にしながら、上記の画像のように好き/嫌い、得意/不得意の軸をつくり、事前にマップにぺこぺこスキルを貼り付けていってもらいました。その後、みんなでわいわいそれについて会話してみたんですが、もうこの時点でちょっと面白かったです。

  • Aさん、ここの領域じつは強かったんだ?
  • ここの部分ってすごい得意そうだけど、好きではなかったのか…。
  • こういうのBさん本当に興味ないですよね知ってました。
  • Cさんそのスキル、もっとずっと得意でいいと思う。

みたいな話がたくさん出てきて、単純にチームの相互理解につながるというか。特にぼくは入社して3ヶ月の新米ですから、みなさんのスキル傾向が知れるだけでもありがたい。そしてきっと、今後入社してくれる方にも、このスキルマップを見せればもう誰がなにが得意なのかまるわかり!やった!そういう意味でも有意義なマップを作ることができました。

個人のスキルマップから、チームのスプレッドシートへ

スキルマップを作ったことにより、みんなのスキル傾向はわかりました。でもそれって、結局は個人のマップでしかなく、チームとしてどうなのかは把握できないんですよね(人数多くないのでふんわり把握できるんですが、それはそれ)。なので、スプレッドシートでカウントして、得意な人が多いスキル、得意な人が少ないスキルなどをピックアップしてみました。

f:id:unifa_tech:20201209010635j:plain
赤裸々すぎるので詳細はボカしました

結果としては

  • AfterEffectsやアニメーションなどは、業務として使えてないのでほぼみんな「苦手」ですが、興味や課題感はありそう。5名中、3名が伸ばしたいスキルに。
  • ディレクション系は「得意」な人が複数名いますが、好きな人は少ない。基本的に、やりたくないみたい。
  • パーソナリティ部分の好きはみんなかぶっているので、チームメンバーの気は合っていそう

などの所感が得られました。
いや、実際にはもっといっぱい所感は得られたんですがはしょりました。

AfterEffectsなどは、AdobeMaxでもセッションありましたが、マイクロインタラクションやモーションがみんな興味ありそうですね。つい最近、チーム内の発表でもありましたし。

チームとしての特性もぼんやり見えてきたところではあるので、このシートについてみんなで少し会話をしつつ、ではどんな人が来てくれたら嬉しいのか?という点でディスカッションを重ねます。スキルマップをここでも活用していて、来て欲しい人のイメージを浮かべながら、UIデザイン強い人がいいよね、コードは無理にわからなくてもいいよね、などと言いながら、架空の誰かのスキルマップをつくり、人物像を作り込んでいきます。

言語化してペルソナ化

チームメンバーと会話していくうちに、だんだんこんな人かなぁという像は見えているのですが、それをきちんと言語化して、対外的にもわかりやすくするために最終的にペルソナに落とし込んでいきました。ペルソナの作成法もいろいろ手法あるかと思うのですが、今回は古典的な手法である「想☆像☆力」でつくりました。

新デザイナーさんのスキルマップを見ながら想像していきます。ビジュアルデザインがこれだけ強いということは、事業会社よりも制作会社でデザイナーやってたんじゃないかな。チーム開発がこの位置だから、きっと事業会社の経験もそこそこあって。昔はSketchを使ってたけど、最近ではXDで……などなど。

結果、こんな感じの方をチームでは求めているということに。

f:id:unifa_tech:20201209011445j:plain
赤裸々すぎたのでボカしました

採用にペルソナがあるとはかどる?

一般的なペルソナの効果として、複数の関係者のイメージが共有される、判断に迷ったときの指針になる、などがあるかと思いますが、これは採用でも効果があります。

こちらからアプローチをかける際にも、イメージが統一しているのでブレがない。書類選考でも、必要なスキルレベルがはっきりしているので、判断に迷いがない。旗をたてる効果のあるペルソナ作成は、採用でも有効に作用しました。

ただ、ペルソナはあくまでもその時ほしい人材のイメージでしかないため、今後はペルソナも更新をかけていく必要があります。このペルソナを作ってから時間も少し経ち、ぼく個人としても違う印象を持っています。また、スキルマップそのものも、同じスキルで3年後もいるわけがないと思うので、定期的に見直しが必要です。今まで好きだったスキルが、なぜか嫌いに落っこちているケースもあるでしょう。人には歴史があります。

ペルソナ作成はそれなり時間がかかりますが、指針を決めておくことは大きな意味があると思います。広く採用活動をはじめる際には、少し時間をとってチーム内で対話してみるのはいかがでしょうか? 副産物として、チーム内の理解が深まります!

最後ですが、これずっと採用の話をしていたわけなので、つまりデザイナー募集しています。デザイナーだけじゃなく募集しています。詳しくは下記からご確認ください。

それではよいお年を!

unifa-e.com

上司「たじーには恐怖心が足りない」

f:id:unifa_tech:20201208014321p:plain
※「間違っているかもしれない」と自問自答によってアウトプットの質を高めよ、というフィードバックです。
こんにちは、プロダクトマネージャーの田嶋(たじー)です。
この記事は、UniFaアドベントカレンダーの12日目の記事となります。

今回のテーマは、「意思決定に伴う責任と、向き合い方」についてです。タイトルにある通り、今回は上司からのフィードバックを基にした振り返りです。まだまだ至らないことばかりですが、自戒のために記録します。

続きを読む

僕がファシリテーションをするときに意識している13のこと

こんにちは。スクラムマスターの渡部です。

この記事は、UniFa Advent Calendarの記事でもあり、ファシリテーター Advent Calendarの記事でもあります。

さて、本題であるファシリテーションに触れる前に、誤解の無いよう(期待値調整のために)少し僕自身についてお話しておこうと思います。

はじめに、僕はファシリテーションを専門に働いている訳ではありません。

スクラムマスター 兼 プロダクトマネージャーとしていろんなMTGに参加していたり、最近トライし始めた新たな試みとして、6つの開発チームでふりかえり導入支援をしていたりと、「話したりファシリテーションする機会」がちょっと多いだけの人です。

今回は、そんな僕がいろんなMTGでファシリテーションするときに気をつけていることのご説明と、実際にそのMTGに参加していた人に「実際どうだったか?」「どう思ったか?」などをインタビューしてご紹介していきます!

ファシリテーションするときに意識していること一覧

結構たくさんあったので、箇条書きで列挙しておきます。

各項目についての詳細説明は、次の章で行っていきます。

事前〜MTGの冒頭でやること
  • そもそも僕たちは何のために集まっているのかを明確にする
  • 今日、いま、この時間で達成したいことを明確にする
  • 今日、いま、この時間で話さないこと、気にしないことを明確にする
  • どんな動きを期待しているかを、各々に明確に伝える
  • チェックイン
MTG中にやること
  • アイデアを出すときは、シンキングタイムを明確に取る
  • 自分が感じ取ったことを素直に伝える
  • 発言という行為そのものに対して感謝し、歓迎する
  • 発言内容が分かりにくい場合は、感謝を伝えた上で、追加の説明を求める
  • それでも分かりにくい場合は、言い換え確認・具体例で確認する
  • 「アイデアをたくさん出す時間」と「1つのアイデアを深ぼる時間」は明確に分ける
  • その場で話すべきでないことは、避難させる
  • 意見が採用されない場合は特に、理由や考え方を即、明確にフィードバックする
続きを読む